skip to Main Content

I have an array which consists of another array of objects with key value pairs. I want to merge and segregate these array of objects based on key which is common in every object. There can be n number of array of objects.

array1 = [
  [ 
    { Time: 45, Element: 'Hi'},
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' }
  ],
  [
    { Time: 45, Element: 'Hi' }, 
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' },
    { Time: 25, Element: 'Hello' }, 
    { Time: 35, Element: 'Hello' }, 
    { Time: 20, Element: 'Hello' }
  ],
  [
    { Time: 45, Element: 'Hi' },
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' },
    { Time: 25, Element: 'Hello' },
    { Time: 35, Element: 'Hello' },
    { Time: 20, Element: 'Hello' },
    { Time:100, Element: 'Bye'}
  ]
];

What i expect from the merged array is as follows:

mergedArray = [
  [ 
    { Time: 45, Element: 'Hi'},
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' }
  ],
  [
    { Time: 25, Element: 'Hello' }, 
    { Time: 35, Element: 'Hello' }, 
    { Time: 20, Element: 'Hello' }
  ],
  [
    { Time:100, Element: 'Bye'}
  ]
];

How to achieve this in Javascript?

I tried the following code

arrays = [
  [ 
    { Time: 45, Element: 'Hi'},
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' }
  ],

  [
    { Time: 45, Element: 'Hi' }, 
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' },
    { Time: 25, Element: 'Hello' }, 
    { Time: 35, Element: 'Hello' }, 
    { Time: 20, Element: 'Hello' }
  ],
  [
    { Time: 45, Element: 'Hi' },
    { Time: 55, Element: 'Hi' },
    { Time: 65, Element: 'Hi' },
    { Time: 25, Element: 'Hello' },
    { Time: 35, Element: 'Hello' },
    { Time: 20, Element: 'Hello' },
    { Time:100, Element: 'Bye'}
  ]
];

const uniqueMergedArray = arrays.reduce((result, obj) => {
  if (!result.some(item => item.Element === obj.Element)) {
    result.push(obj);
  }
  return result;
}, [])

console.log("Merged Array:", uniqueMergedArray);

The above code only return the first array. Please suggest a simple method to merge the array of objects and get three different unique arrays with no duplicates.

5

Answers


  1. The obj in your .reduce() callback represents one of your inner arrays, not the objects, and so you’re not properly iterating the inner arrays and checking the objects. I’d suggest using .map() within an inner .filter(), with an additional filter() at the end to remove the empty arrays:

    const arrays = [ [{Time: 45,Element:'Hi'},{Time: 55, Element: 'Hi' },{ Time: 65, Element: 'Hi' }], [{Time: 45,Element:'Hi' }, { Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }], [{Time: 45,Element:'Hi' },{ Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello'},{Time: 35, Element: 'Hello'},{Time: 20, Element: 'Hello'},       {Time:100, Element: 'Bye'}] ];
    
    const timeElementSet = new Set();
    const uniqueMergedArray = arrays.map(arr => arr.filter(obj => {
      const key = `${obj.Time}$${obj.Element}`;
      if (timeElementSet.has(key)) return false;
      timeElementSet.add(key);
      return true;
    })).filter(arr => arr.length > 0);
    
    console.log("Merged Array:", uniqueMergedArray);

    The above uses .map() to transform each inner array into a filtered version of itself. Within the filter function, I’m using a Set to keep track of Time and Element pairs that have been already seen by creating a serialized version of the object in the shape of Time$Element. This allows us to check if the object has already been seen already, which we can then use to return true/false to keep the current element or not.


    If you want, you can merge the .map().filter() into one reduce() call to avoid the additional iteration over the array (but this isn’t as readable in my opinion):

    const arrays = [ [{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }], [{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }, { Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }], [{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }, { Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }, { Time: 100, Element: 'Bye' }] ];
    
    const timeElementSet = new Set();
    const uniqueMergedArray = arrays.reduce((acc, curr) => {
      const filtered = curr.filter(obj => {
        const key = `${obj.Time}$${obj.Element}`;
        if (timeElementSet.has(key)) return false;
        timeElementSet.add(key);
        return true;
      });
      return filtered.length > 0 ? [...acc, filtered] : acc; // or use: if(...) acc.push(filtered); return acc;` to be more efficient
    }, []);
    
    console.log("Merged Array:", uniqueMergedArray);
    Login or Signup to reply.
  2. You could take a Set and filter inner arrays.

    const
        data = array1 = [[{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }], [{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }, { Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }], [{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }, { Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }, { Time: 100, Element: 'Bye' }]],
        seen = new Set,
        result = data.reduce((r, a) => {
            a = a.filter(o => {
                const item = ['Time', 'Element'].map(k => o[k]).join('|');
                if (!seen.has(item)) {
                    seen.add(item);
                    return true;
                }
            });
            if (a.length) r.push(a);
            return r;
        }, []);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
  3. You can simply utilize the Set data structure to avoid duplicates and reduce() to process the array.

    const array1 = [[{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' }],[{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' },{ Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }],[{ Time: 45, Element: 'Hi' }, { Time: 55, Element: 'Hi' }, { Time: 65, Element: 'Hi' },{ Time: 25, Element: 'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' },{ Time: 100, Element: 'Bye' }]];
    
    function mergeAndSeg(arr) {
      const mergedObjects = arr.reduce((acc, subArray) => {
        subArray.forEach(obj => {
          const key = obj.Element;
          if (!acc[key]) {
            acc[key] = new Set();
          }
          acc[key].add(JSON.stringify(obj));
        });
        return acc;
      }, {});
    
      const mergedArray = [];
      for (let key in mergedObjects) {
        mergedArray.push([...mergedObjects[key]].map(JSON.parse));
      }
    
      return mergedArray;
    }
    
    const mergedArray = mergeAndSeg(array1);
    console.log(mergedArray);
    Login or Signup to reply.
  4. So you map the arrays into a new one with the child arrays reduced. For the mapping function you provide a map variable in a closure of IIFE where we store our object keys encountered during the reducing.

    We use ??= operator to add the child array items to the map if they are not in the map yet. Also we add it to the reduced array in that case. So basically an item with a new key is added to the result then items with the same key are skipped since they are already in the map and the right part of the ??= operator isn’t executed.

    A trick here is not make a key by concatenating item values thus making a new string (that slow) but rather make nested mapping with ??=.

    Finally we filter the final array of the reduced child arrays with length property (it should be truthy).

    const arrays = [ [{Time: 45,Element:'Hi'},{Time: 55, Element: 'Hi' },{ Time: 65, Element: 'Hi' }], [{Time: 45,Element:'Hi' }, { Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }], [{Time: 45,Element:'Hi' },{ Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello'},{Time: 35, Element: 'Hello'},{Time: 20, Element: 'Hello'},       {Time:100, Element: 'Bye'}] ];
    
    
    const out = arrays
      .map( 
        (map => arr => arr.reduce((r, obj) => {
          (map[obj.Time] ??= {})[obj.Element] ??= r[r.length] = obj;
          return r;
        }, []))({})
       )
      .filter(({length}) => length);
    
    console.log(...out.map(JSON.stringify));
    ` Chrome/120
    ---------------------------------------------------------
    Alexander     1.00x  |  x1000000  368  407  410  445  466
    Nick filter   1.65x  |  x1000000  608  627  649  669  684
    Nick reduce   1.73x  |  x1000000  638  674  688  698  709
    Nina          3.99x  |   x100000  147  147  153  153  169
    ---------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const arrays = [ [{Time: 45,Element:'Hi'},{Time: 55, Element: 'Hi' },{ Time: 65, Element: 'Hi' }], [{Time: 45,Element:'Hi' }, { Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello' }, { Time: 35, Element: 'Hello' }, { Time: 20, Element: 'Hello' }], [{Time: 45,Element:'Hi' },{ Time: 55, Element: 'Hi'},{Time: 65, Element: 'Hi' }, {Time: 25,Element:'Hello'},{Time: 35, Element: 'Hello'},{Time: 20, Element: 'Hello'},       {Time:100, Element: 'Bye'}] ];
    
    // @benchmark Nick filter
    {
    const timeElementSet = new Set();
    arrays.map(arr => arr.filter(obj => {
      const key = obj.Time + '$' + obj.Element;
      if (timeElementSet.has(key)) return false;
      timeElementSet.add(key);
      return true;
    })).filter(arr => arr.length > 0);
    }
    
    // @benchmark Nick reduce
    {
    const timeElementSet = new Set();
    arrays.reduce((acc, curr) => {
      const filtered = curr.filter(obj => {
        const key = obj.Time + '$' + obj.Element;
        if (timeElementSet.has(key)) return false;
        timeElementSet.add(key);
        return true;
      });
      return filtered.length > 0 ? [...acc, filtered] : acc; // or use: if(...) acc.push(filtered); return acc;` to be more efficient
    }, []);
    }
    
    // @benchmark Nina
    {
        const seen = new Set;
        arrays.reduce((r, a) => {
            a = a.filter(o => {
                const item = ['Time', 'Element'].map(k => o[k]).join('|');
                if (!seen.has(item)) {
                    seen.add(item);
                    return true;
                }
            });
            if (a.length) r.push(a);
            return r;
        }, []);
    }
    
    // @benchmark Alexander
    arrays.map((map => arr => arr.reduce((r, obj) => ((map[obj.Time] ??= {})[obj.Element] ??= r[r.length] = obj, r), []))({})).filter(({length}) => length);
    
    /*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));
    Login or Signup to reply.
  5. The code you provided does not accomplish the task because it attempts to reduce a nested array structure without flattening it and does not handle the segregation of objects based on their ‘Element’ property.

    If you’re lookin’ to split up an array of objects by a common key, you can totally boss it with a combo of reduce and map. You can create a Map object to group items by their Element value and then extract the grouped items into separate arrays.

    The code provided below just merges the arrays, and also removes their duplicates and you achieve the desired result.

    const arrays = [
      [ 
        { Time: 45, Element: 'Hi'},
        { Time: 55, Element: 'Hi' },
        { Time: 65, Element: 'Hi' }
      ],
      [
        { Time: 45, Element: 'Hi' }, 
        { Time: 55, Element: 'Hi' },
        { Time: 65, Element: 'Hi' },
        { Time: 25, Element: 'Hello' }, 
        { Time: 35, Element: 'Hello' }, 
        { Time: 20, Element: 'Hello' }
      ],
      [
        { Time: 45, Element: 'Hi' },
        { Time: 55, Element: 'Hi' },
        { Time: 65, Element: 'Hi' },
        { Time: 25, Element: 'Hello' },
        { Time: 35, Element: 'Hello' },
        { Time: 20, Element: 'Hello' },
        { Time: 100, Element: 'Bye'}
      ]
    ];
    
    function foo(arrayofarrays) {
      const flatArray = arrayofarrays.flat();
    
      const grouped = flatArray.reduce((acc, obj) => {
        const key = obj.Element;
        if (!acc.has(key)) {
          acc.set(key, { items: [], seenTimes: new Set() });
        }
        let group = acc.get(key);
        if (!group.seenTimes.has(obj.Time)) {
          group.items.push(obj);
          group.seenTimes.add(obj.Time);
        }
        return acc;
      }, new Map());
    
      const mergedArray = Array.from(grouped.values()).map(group => group.items);
    
      return mergedArray;
    }
    
    const mergedArray = foo(arrays);
    console.log(mergedArray);

    Output:

    [
      [
        { Time: 45, Element: 'Hi' },
        { Time: 55, Element: 'Hi' },
        { Time: 65, Element: 'Hi' }
      ],
      [
        { Time: 25, Element: 'Hello' },
        { Time: 35, Element: 'Hello' },
        { Time: 20, Element: 'Hello' }
      ],
      [ { Time: 100, Element: 'Bye' } ]
    ]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search