0

I have two arrays of equal length, each does contain object data.

This is the example code of the first array ...

const array1 = [{
  key: '5',
  value: '550',
}, {
  key: '6',
  value: '750',
}];

And here is the code for the second one ...

const array2 = [{
  type: 'Job',
  status: 'Finished',
  key : '5',
}, {
  type: 'Ticket',
  status: 'Processing',
  key : '6',
}];

In order to further process my data I need an intersection of both arrays with their corresponding object items being merged. The result should look like this ...

[{
  key: '5',
  value: '550',
  type: 'Job',
  status: 'Finished',
}, {
  key: '6',
  value: '750',
  type: 'Ticket',
  status: 'Processing',
}]

What I have come up with so far is ..

array1.forEach(function (element) {
  array2.forEach(function (element) {
    return {
      type: 'Ticket',
      status: 'Processing'
    };
  });
});

I don't know how to create the expected result. How would a solution to my problem look like?

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
samanthas
  • 79
  • 1
  • 7

3 Answers3

2

You could store the objects in a hash table by key and merge the other array with the data from the hash table.

const
    array1 = [{ key: '5', value: '550' }, { key: '6', value: '750' }],
    array2 = [{ type: 'Job', status: 'Finished', key: '5' }, { type: 'Ticket', status: 'Processing', key: '6' }],
    temp = Object.fromEntries(array2.map(o => [o.key, o])),
    result = array1.map(o => ({ ...o, ...temp[o.key] }));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • the merging can be done within a single `map` iteration (cycle). – Peter Seliger Dec 23 '20 at 14:29
  • but only if the array have the same order of `key`. – Nina Scholz Dec 23 '20 at 14:37
  • @samanthas, the hash table has only the value as key, independently of the name. the later mapping takes the other name for the hash table. the result contain both keys, as long as you want only one and select which one. to get only a single key with the same value, you could remove one of the keys by destructuring the object, like `({ unwantedKey, ... o }) => ...` – Nina Scholz Dec 23 '20 at 15:22
1

It sounds like you need to combine objects in two arrays by their key. Array methods are a good choice, but you should also look into other methods.

const combinedItemsArray = array1.map(item1 => {
       const matchingItem = array2.find(item2 => item2.key === item1.key) 
       return {...item1, ...matchingItem }
})

This uses the spread operator to combine the values of items with matching keys.

Some things to consider:

  • Array.find only matches the first item in array 2 that satisfies the result. So if the arrays have items with duplicate keys, you need to modify this.

  • This will break down on a performance level if you have lots of items in your arrays (10s of 1000's). In that case the hash table answer might be a better approach.

  • its a great idea to refer to the common array methods often on mdn. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map use the sidebar to find the one that you might need!

Pedro Ferreira
  • 852
  • 9
  • 23
1

Make use of Array.prototype.map which can be passed an additional target object to ... here one would use the second array from which one wants to retrieve the corresponding merger item.

The merging is done via Object.assign where one does merge both array items to a newly created object in order to not mutate any of the merger items of either involved array ...

const array1 = [{
  key: '5',
  value: '550',
}, {
  key: '6',
  value: '750',
}];

const array2 = [{
  type: 'Job',
  status: 'Finished',
  key : '5',
}, {
  type: 'Ticket',
  status: 'Processing',
  key : '6',
}];

function mergeWithSameIndexItemFromBoundArray(item, idx) {
  const boundArray = this;
  return Object.assign({}, item, boundArray[idx]);
}

console.log(
  'merge result of array2 and array1 ...',
  array2.map(mergeWithSameIndexItemFromBoundArray, array1)
);
console.log(
  'merge result of array1 and array2 ...',
  array1.map(mergeWithSameIndexItemFromBoundArray, array2)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

In case of arrays of non matching item orders one can build on the above approach of providing an additional target object to map.

This time it will not be the second array but a transformation of it; an object which has mapped all of this second array's items to it by using the very property name by which all the items are identified. For the OP's example this property name is key.

Thus one needs to write an additional function which does this mapping task. The next following example chooses an approach based on Array.prototype.reduce.

In addition the mapper function has to be rewritten in order to make use of the before created bound map by simply using an array item's key property instead of the array index from before ...

const array1 = [{
  key: '5',
  value: '550',
}, {
  key: '7',
  value: '320',
}, {
  key: '6',
  value: '750',
}];

const array2 = [{
  type: 'Ticket',
  status: 'Processing',
  key : '6',
}, {
  type: 'Job',
  status: 'Finished',
  key : '5',
}, {
  type: 'Task',
  status: 'Pending',
  key : '7',
}];

function createKeyBasedItemMap(map, item) {
  map[item.key] = item;
  return map;
}
function mergeWithKeyBasedItemFromBoundMap(item) {
  return Object.assign({}, item, this[item.key]);
}

console.log(
  'map-based merge of unordered array2 and array1 items ...',
  array2.map(
    mergeWithKeyBasedItemFromBoundMap,
    array1.reduce(createKeyBasedItemMap, {})
  )
);
console.log(
  'map-based merge of unordered array1 and array2 items ...',
  array1.map(
    mergeWithKeyBasedItemFromBoundMap,
    array2.reduce(createKeyBasedItemMap, {})
  )
);
console.log('\n');

console.log(
  'proof of concept ... the item-map based on array1 ...',
  array1.reduce(createKeyBasedItemMap, {})
);
console.log(
  'proof of concept ... the item-map based on array2 ...',
  array2.reduce(createKeyBasedItemMap, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • 1
    hm, sorting has at least O(nlogn) plus the rest. – Nina Scholz Dec 23 '20 at 15:35
  • @NinaScholz ... I did it, my second approach still uses the additional target object. This time it is an object/map instead of an array/list. Generating this map is done by a single `reduce` cycle. Thus an approach which can handle matching and non matching item orders can be achieved with just two iteration cycles ... `reduce` and `map`. Thanks for pushing me. – Peter Seliger Dec 23 '20 at 16:37