Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I calculate percentages of match between 2 Javascript objects?

Tags:

javascript

I have a need to calculate percentages of match between 2 Javascript objects. How can I achieve this?

Examples:

obj1={key1: "val1", key2: "val2", key3: "val3", key4: "val4"}
obj2={key5: "val5", key6: "val6"}
//result should be: 0%


obj1={key1: "val1", key2: "val2", key3: "val3", key4: "val4"}
obj2={key1: "val1", key2: "val2"}
//result should be: 50%
like image 770
Dhekra ZELAITI Avatar asked Feb 10 '17 15:02

Dhekra ZELAITI


1 Answers

You have not specified exactly how you want such a percentage calculated. There are at least four different ways of measuring percent similarity between two objects:

  • the percent of the keys in the 1st object that can be found in the 2nd,
  • the percent of the values in the 1st object that can be found in the 2nd, including duplicates,
  • the percent of the values in the 1st object that can be found in the 2nd, with no duplicates allowed, and
  • the percent of {key:value} pairs in the 1st object that can be found in the 2nd object.

Note that order of the two objects matters, i.e. calling with (obj1,obj2) may produce different results than calling with (obj2,obj1).

The second option above counts each instance of repeated values in the 2nd object, making it possible to return values higher than 100%, e.g. comparing {a:42} and {x:42, y:42} returns 200%. This might not initially seem reasonable but is mathematically valid based on how the calculation is defined. Such duplicates are not allowed in the third option, such that comparing {a:42} and {x:42, y:42} returns 100%. In either case, duplicate values in the first object are not counted, e.g. comparing {a:42, b:42} and {x:42} yields 50% in both cases. Such duplicates are not a relevant consideration for the first and fourth options above as JavaScript objects cannot have repeated keys.

The code snippet below demonstrates all four methods.

const obj1 = {a: 1, b: 2, c: 3, d: 3};
const obj2 = {a: 1, b: 1, e: 2, f: 2, g: 3, h: 5};

const keys = obj => Object.keys(obj);
const toPct = a => 100 / keys(a).length;

const pctSameKeys = (a, b) =>
  keys(b).reduce((num,key) =>
    keys(a).indexOf(key) > -1 ? num + 1 : num, 0) * toPct(a);

const pctSameValuesIncludingDuplicates = (a, b) =>
  keys(b).map(key => b[key]).reduce((num,v) =>
    keys(a).map(key => a[key]).indexOf(v) > -1 ? num + 1 : num, 0) * toPct(a);

const pctSameValuesNoDuplicates = (a, b) =>
  Array.from(new Set(keys(b).map(key => b[key]))).reduce((num,v) =>
    keys(a).map(key => a[key]).indexOf(v) > -1 ? num + 1 : num, 0) * toPct(a);

const pctSameProps = (a, b) =>
  keys(b).reduce((num,key) =>
    a[key] === b[key] ? num + 1 : num, 0) * toPct(a);

console.log('obj1:', JSON.stringify(obj1));
console.log('obj2:', JSON.stringify(obj2));
console.log('% same keys:                   ', pctSameKeys(obj1, obj2));
console.log('% same values, incl duplicates:', pctSameValuesIncludingDuplicates(obj1, obj2));
console.log('% same values, no duplicates:  ', pctSameValuesNoDuplicates(obj1, obj2));
console.log('% same properties (k/v pairs): ', pctSameProps     (obj1, obj2));

The following code snippet shows essentially exactly the same thing, but is written in an even more functional style (but see this answer on the Computer Engineering site to see how my code here can potentially be re-written in a still functional but more readable style):

const obj1 = {a: 1, b: 2, c: 3, d: 3};
const obj2 = {a: 1, b: 1, e: 2, f: 2, g: 3, h: 5};

// x or X is key or value or key/value pair

const getXs = (obj, getX) =>
  Object.keys(obj).map(key => getX(obj, key));

const getPctSameXs = (getX, filter) =>
  (objA, objB) =>
    (filter ? filter(getXs(objB, getX)) : getXs(objB, getX)).reduce(
      (numSame, x) =>
        getXs(objA, getX).indexOf(x) > -1 ? numSame + 1 : numSame,
      0
    ) / Object.keys(objA).length * 100;

const pctSameKeys       = getPctSameXs((obj, key) => key);
const pctSameValsDups   = getPctSameXs((obj, key) => obj[key]);
const pctSameValsNoDups = getPctSameXs((obj, key) => obj[key], vals => [...new Set(vals)]);
const pctSameProps      = getPctSameXs((obj, key) => JSON.stringify( {[key]: obj[key]} ));

console.log('obj1:', JSON.stringify(obj1));
console.log('obj2:', JSON.stringify(obj2));
console.log('% same keys:                   ', pctSameKeys      (obj1, obj2));
console.log('% same values, incl duplicates:', pctSameValsDups  (obj1, obj2));
console.log('% same values, no duplicates:  ', pctSameValsNoDups(obj1, obj2));
console.log('% same properties (k/v pairs): ', pctSameProps     (obj1, obj2));
like image 117
Andrew Willems Avatar answered Oct 08 '22 22:10

Andrew Willems