0

I am trying to merge two or more arrays but its not working as I am wanting.

I have two arrays arrCustomer and arrCustomerDetails. Both arrays have CustomerID as a key but I am wanting all values from arrCustomerDetails with merged in properties from the array arrCustomer. I tried using _merge but its giving my only the count of the arrCustomer.

Example:

const arrCustomer = [
    { id: 1, name: "a" },
    { id: 2, name: "b" },
    { id: 3, name: "c" }
];

const arrCustomerDetail = [
    { id: 1, location: "jupiter", group: "C" },
    { id: 1, location: "venus", group: "G" },
    { id: 2, location: "mars", group: "D" }
];

const expecteResult = [
    { id: 1, name: "a", location: "jupiter", group: "C" },
    { id: 1, name: "a", location: "venus", group: "G" },
    { id: 2, name: "b", location: "mars", group: "D" },
    { id: 3, name: "c"  location: "", group: "" }
]

This works but its slow:

 let combinedData = [];
      arrCustomerDetail.map((element) => {
        combinedData.push({
          ...element,
          ...arrCustomer.find(
            (customer) => customer.id=== element.id
          ),
        });
      });

      arrCustomer.map((customer) => {
        if (
          combinedData.findIndex(
            (detail) => detail.id=== customer.id
          ) === -1
        )
          combinedData.push({
            ...customer,
          });
      });

Thanks all

beantownace
  • 117
  • 1
  • 10
  • 1
    Is there a reason you specifically need to use lodash? For a non-lodash way have a look at this: https://stackoverflow.com/questions/46849286/merge-two-array-of-objects-based-on-a-key – marno11 May 27 '20 at 23:01
  • perfect thanks Marno – beantownace May 27 '20 at 23:09
  • okay so I think I have this wrong if there are no records in the arrCustomerDetails I still want all the data from arrCustomer. Is there a way to put it all together if I want both sides essentially? – beantownace May 27 '20 at 23:35
  • So if you have extra ID's in one array or the other? Perhaps have a look at this answer on that same question https://stackoverflow.com/questions/46849286/merge-two-array-of-objects-based-on-a-key#answer-60365470 – marno11 May 27 '20 at 23:39

2 Answers2

0

( Join / Merge / Combine ) multiple arrays of objects combined by key.

Update, it is also possible to make this join at run time for more than one array. you just need to define a primary matrix and create a filter on a matrix of matrices, follow this example in.

I hope this example helps, it worked perfectly, for the examples you gave, just edit the example below and you will see the result, I even made a joke doing the merging of everything and it worked correctly. Remembering that it will always look at a primary matrix as a base.

"use strict";
const arrCustomer1 = [
    { id: 1, name: "a" },
    { id: 2, name: "b" }
];
const arrCustomerDetail1 = [
    { id: 1, location: "jupiter", group: "C" },
    { id: 2, location: "mars", group: "D" }
];
const wantedArray1 = [
    { id: 1, name: "a", location: "jupiter", group: "C" },
    { id: 2, name: "b", location: "mars", group: "D" }
];
const arrCustomer2 = [
    { id: 1, name: "a" },
    { id: 2, name: "b" },
    { id: 3, name: "c" }
];
const arrCustomerDetail2 = [
    { id: 1, location: "jupiter", group: "C" },
    { id: 2, location: "mars", group: "D" }
];
const wantedArray2 = [
    { id: 1, name: "a", location: "jupiter", group: "C" },
    { id: 2, name: "b", location: "mars", group: "D" },
    { id: 3, name: "c" } //or blanks for the other fields location group
];
const arrCustomer3 = [
    { id: 1, name: "a" },
    { id: 2, name: "b" },
    { id: 3, name: "c" }
];
const arrCustomerDetail3 = [
    { id: 1, location: "jupiter", group: "C" },
    { id: 1, location: "venus", group: "G" },
    { id: 2, location: "mars", group: "D" }
];
const wantedArray3 = [
    { id: 1, name: "a", location: "jupiter", group: "C" },
    { id: 1, name: "a", location: "venus", group: "G" },
    { id: 2, name: "b", location: "mars", group: "D" },
    { id: 3, name: "c" } //or blanks for the other fields location group
];

const combinedArrays = [arrCustomerDetail1,
    wantedArray1,
    arrCustomer2,
    arrCustomerDetail2,
    wantedArray2,
    arrCustomer3,
    arrCustomerDetail3,
    wantedArray3];

function joinArrByKey(primaryArr, combinedArrays, key) {
    return primaryArr.map((eArr1) => {
        const filter = new Array().concat.apply([], combinedArrays).filter((eArr2) => eArr2.id === eArr1[key]);
        return filter.length ? Object.assign({}, eArr1, ...filter) : eArr1;
    });
}

console.log(joinArrByKey(arrCustomer1, combinedArrays, 'id'));

console.log(joinArrByKey(arrCustomer2, combinedArrays, 'id'));

console.log(joinArrByKey(arrCustomer3, combinedArrays, 'id'));

( Join / Merge / Combine ) Only for two arrays by key id, faster.

you can create your own function like this and share it with all applications, instead of using _lodash.

"use strict";
const arr1 = [
    { id: 1, name: "a" },
    { id: 2, name: "b" },
];
const arr2 = [
    { id: 1, price: 10 },
    { id: 2, price: 20 },
];
function joinArrById(primaryArr, secondArray) {
    return primaryArr.map((eArr1) => {
        const find = secondArray.find((eArr2) => eArr2.id === eArr1.id);
        return find ? Object.assign(Object.assign({}, eArr1), find) : eArr1;
    });
}
console.log(joinArrById(arr1, arr2));

If the solution above doesn't suit you, a solution with _lodash.

import * as _ from "lodash";

const arr1 = [
  { id: 1, name: "a" },
  { id: 2, name: "b" },
];
const arr2 = [
  { id: 1, price: 10 },
  { id: 2, price: 20 },
];

var arrJoin = _.map(arr1, (obj: { id: number }) => {
  return _.assign(
    obj,
    _.find(arr2, {
      id: obj.id,
    })
  );
});

console.log(arrJoin);
  • Just edited the question with examples so I failed to mention the arrCustomerDetails can have many to one of the arrCustomer. I need to pull in all records from both arrays always and arrCustomer will always only have one id to many possible id's in detail. Also may have a arrCustomer without arrCustomerDetail I also still need. Hope the examples help. – beantownace May 28 '20 at 00:04
  • Problem here is that this solution runs in exponential — O(n^2) —  time. – Nicholas Carey May 28 '20 at 02:21
  • So in the 3rd example which is the big one I am trying to get it is only pulling a 1 to 1 its not getting the second record jupiter and its very slow on 10k records – beantownace May 28 '20 at 02:47
  • I understand the result you want, what you can do is Run one of the functions for arrCustomer in arrCustomerDetail and again for arrCustomerDetail in arrCustomer, then compare the result of these two matrices and remove the duplicates. But this is a little computationally costly. Ideally, you should have only one ID per data list. – Gustavo Damazio May 28 '20 at 11:03
  • I updated a solution got working but its slow I edited the question with what works not sure if I can make this any faster. – beantownace May 28 '20 at 19:14
0

Lodash doesn't really support merging lists together and keying off an object property to do so. I would do something like this, and roll my own merge() function.

[Note: this merge() function runs in linear time — O(n).]

const _ = require('lodash');

function merge(selectKey, keyComparer, ...lists) {
  const map = new Map();

  for (const list of lists) {
    for (const item of list) {

      const key = selectKey(item);
      let value = map.get(key);

      if (value === undefined) {
        map.set(key, [item]);
      }
      else {
        value.push(item);
      }
    }
  }

  const merged = Array.from(map.keys())
    .sort(keyComparer)
    .map( k => _.merge( {}, ...map.get(k) ) );
  return merged;
}

where * selectKey is a function that is passed an object and returns its key, and * keyComparer is a function that accepts two keys, x and y and returns a numeric indication of whether x is less than, equal to, or greater than `y'. The convention is that

  • return values < 0 indicate that x is less than y
  • return values of 0 indicate that x and y are equal
  • return values > 0 indicate x is greater than y

Once you have that, it's easy:

const arr1 = [
  { id: 1, name: 'a' },
  { id: 2, name: 'b' },
  { id: 3, name: 'c' },
];
const arr2 = [
  { id: 1, location: 'jupiter', group: 'C' },
  { id: 4, location: 'venus', group: 'E' },
  { id: 2, location: 'mars', group: 'D' },
];

const idComparer = (x, y) => Math.sign(x - y);
const selectKey = o => o.id;

const merged = merge(selectKey, idComparer, arr1, arr2 );

The return value here is

[
  { id: 1 , name: "a" , location: "jupiter" , group: "C" } ,
  { id: 2 , name: "b" , location: "mars"    , group: "D" } ,
  { id: 3 , name: "c"                                    } ,
  { id: 4 ,             location: "venus"   , group: "E" } ,
]
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • It still does not work if I have the same id 1 for Venus. It is a 1 to Many arr1 to arr2. Runs fast though in O(n) but the issue is I need to pull back all records from arr2 so I will have for example the venus record with id = 1. I would want two records back for id 1: { id: 1, name: 'a', location: jupiter, group: 'C' }, { id: 1, name: 'a', location: venus, group: 'E' } – beantownace May 28 '20 at 03:01
  • @beantownace: so what you are saying is that ID is a *partial key* in the one array, and ID+GROUP is the *full key*. Eminently doable, but I need to think a bit on it. I think what you need is a Map (keyed by ID), whose value is a Map (keyed by Group). Stand by tomorrow! – Nicholas Carey May 28 '20 at 04:31
  • Its a 1 to Many arrCustomer to arrCustomerDetail the only thing is there may be no records in the arrCustomerDetail for that customer and I still need to pickup the same so in your results the { id: 3 , name: "c" } , works great the issue is its not picking up the many arrCustomerDetails – beantownace May 28 '20 at 13:12
  • Updated the question to show an exact result I would be looking for so its a 1 to Many will always have arrCustomer the details would be the many. – beantownace May 28 '20 at 14:38