Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript compare multiple arrays of objects

How can I compare multiple arrays of objects and add new properties with the number of occurrences an object was found and the array indexes where the object was found? The object comparison must be made by the name property.

Example:

var arrays = [
  [
    {
      name: 'aa',
      value: 1
    },
    {
      name: 'ab',
      value: 2
    },
    {
      name: 'ac',
      value: 3
    },
    {
      name: 'aa',
      value: 1
    }
  ],
  [
    {
      name: 'aa',
      value: 1
    },
    {
      name: 'ab',
      value: 2
    },
  ],
  [
    {
      name: 'ac',
      value: 3
    },
    {
      name: 'aa',
      value: 1
    }
  ]
]

After execution the object from the above array should have these properties:

[
  [
    {
      name: 'aa',
      value: 1,
      occurrences: 3,
      where: [0, 1, 2]
    },
    {
      name: 'ab',
      value: 2,
      occurrences: 2,
      where: [0, 1]
    },
    {
      name: 'ac',
      value: 3,
      occurrences: 2,
      where: [0, 2]
    },
    {
      name: 'aa',
      value: 1,
      occurrences: 3,
      where: [0, 1, 2]
    }
  ],
  [
    {
      name: 'aa',
      value: 1,
      occurrences: 3,
      where: [0, 1, 2]
    },
    {
      name: 'ab',
      value: 2,
      occurrences: 2,
      where: [0, 1]
    }
  ],
  [
    {
      name: 'ac',
      value: 3,
      occurrences: 2,
      where: [0, 2]
    },
    {
      name: 'aa',
      value: 1,
      occurrences: 3,
      where: [0, 1, 2]
    }
  ]
]

Basically I want to check if the object with a specific name property exists in the other arrays.

This is the solution that comes in my mind: 1. Loop through the array that has the most objects
2. Loop through each object
3. Loop through the other arrays and apply Array.prototype.find()
But this will take a lot of time since each of my array will have at least 500 objects...

like image 796
Valip Avatar asked Oct 29 '22 22:10

Valip


2 Answers

You can use array#reduce to get the number of occurrences and the index of occurrences in an object.

Then, you can modify your object in the arrays by simply using Object.assign() and adding the where and occurrences property.

var arrays = [ [ { name: 'aa', value: 1 }, { name: 'ab', value: 2 }, { name: 'ac', value: 3 }, { name: 'aa', value: 1 } ], [ { name: 'aa', value: 1 }, { name: 'ab', value: 2 }, ], [ { name: 'ac', value: 3 }, { name: 'aa', value: 1 } ] ];

var result = arrays.reduce((res, arr, index) => {
  arr.forEach(({name,value}) => {
    res[name] =  res[name] || {occurrences: 0};
    res[name]['where'] = res[name]['where'] || [];
    if(!res[name]['where'].includes(index)){
      res[name]['where'].push(index);
      res[name].occurrences += 1;
    }
  });
  return res;
},{});

arrays.forEach(arr => arr.forEach(obj => Object.assign(obj, result[obj.name])));
console.log(arrays);
like image 189
Hassan Imam Avatar answered Nov 15 '22 07:11

Hassan Imam


This looked like trivial reduce, until i noticed nested arrays ) so it's more like flatten + reduce, with memory.

Code below is doing what you need, just the prop names are short (as i type them on the phone):

let f = (ai, a,v,i,m) => {
    if (!a[v.id]) {
    a[v.id] = {id: v.id, v: v.name, count: 1, at: [ai]};
    } else {
    a[v.id].count += 1;
    a[v.id].at.push(ai);
    }
    return a;
};
let r = [[{id: 'aa', value: 42}], [{id: 'ba', value: 11}, {id: 'aa', value: 42}]]
.reduce ((a, v, i) => v.reduce (f.bind (null, i),a), {}); 
console.log (r);

Code still visits each element in any array only once, so complexity is O(n), and there should not be a problem running it on arrays up to a million of total elements (e.g. 1000 arrays of 1000 elements, or 200 x 5000).

like image 24
c69 Avatar answered Nov 15 '22 06:11

c69