Merge arrays from different objects with same key

Viewed 106

I have the following code:

const blueData = {
    "items": [
        {
            "id": 35,
            "revision": 1,
            "updatedAt": "2021-09-10T14:29:54.595012Z",
        },
    ]
}

const redData = {}

const greenData = {
    "items": [
        {
            "id": 36,
            "revision": 1,
            "updatedAt": "2021-09-10T14:31:07.164368Z",
        }
    ]
}

let colorData = []
colorData = blueData.items ? [colorData, ...blueData.items] : colorData
colorData = redData.items ? [colorData, ...redData.items] : colorData
colorData = greenData.items ? [colorData, ...greenData.items] : colorData

I am guessing the spread operator is not the right approache here as I'm getting some extra arrays in my final colorData array. I simply want to build a single array of 'items' that contains all of the 'items' from the 3 objects.

Here's a link to that code in es6 console: https://es6console.com/ktkhc3j2/

5 Answers

Put your data into an array then use flatMap to unwrap each .items:

[greenData, redData, blueData].flatMap(d => d.items ?? [])
//=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
//=> , {id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z'}]

If you fancy you could abstract d => d.items ?? [] with a bit of curry (no pun intended ;)

const take = k => o => o[k] ?? [];

Which gives us:

[greenData, redData, blueData].flatMap(take('items'))

We can even go a step further if you ever need to repeat this process with different keys:

const concatBy = fn => xs => xs.flatMap(x => fn(x));

Now it almost feels like you're expressing your intent with words instead of code:

const takeItems = concatBy(take('items'));

takeItems([greenData, redData, blueData]);
//=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
//=> , {id: 35, revision: 1, updatedAt: '2021-09-

Let's build another function:

const takeFood = concatBy(take('food'));

takeFood([{food: ['', '']}, {food: ['', '']}]);
//=> ['', '', '', '']

Addendum

This is only meant as a potentially useful learning material. My advice is to use flatMap.

This:

[[1, 2], [3, 4]].flatMap(x => x)
//=> [1, 2, 3, 4]

Can also be expressed with reduce. Slightly more verbose but does what it says on the tin:

[[1, 2], [3, 4]].reduce((xs, x) => xs.concat(x), [])
//=> [1, 2, 3, 4]

So to put it simply you could also do:

[greenData, redData, blueData].reduce((xs, x) => xs.concat(x.items ?? []), [])

You can do this using the Logical OR operator which lets you provide a default value if the items field is missing.

const blueData = { items: [ { id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z', }, ], };
const redData = {};
const greenData = { items: [ { id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z', }, ], };

const colorData = [
  ...(blueData.items || []),
  ...(redData.items || []),
  ...(greenData.items || []),
];

console.log(colorData);

Maybe I'm a little old-fashioned but I'd use concat for that:

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

const blueData = {
    "items": [
        {
            "id": 35,
            "revision": 1,
            "updatedAt": "2021-09-10T14:29:54.595012Z",
        },
    ]
}

const redData = {}

const greenData = {
    "items": [
        {
            "id": 36,
            "revision": 1,
            "updatedAt": "2021-09-10T14:31:07.164368Z",
        }
    ]
}


const colorData = [].concat(blueData.items,redData.items,greenData.items).filter(x => x)

console.log(colorData)

the last filter is for removing undefined values

Like this?

colorData = blueData.items ? [...colorData, ...blueData.items] : colorData
colorData = redData.items ? [...colorData, ...redData.items] : colorData
colorData = greenData.items ? [...colorData, ...greenData.items] : colorData

Output:

[{"id":35,"revision":1,"updatedAt":"2021-09-10T14:29:54.595012Z"}, 
{"id":36,"revision":1,"updatedAt":"2021-09-10T14:31:07.164368Z"}]

I think you need to add the spread operator also to the colorData array, because if not you are adding the colorData array itself, not its items.

If you want the simplest solution, you can iterate with a for-loop between all arrays. Create a temporary array that will store data found on each index. This is the fastest and the most flexible solution.

var x1 = {
  "items": [
    { "testKey1": "testVal" }
  ] 
};

var x2 = {
  "items": [
    { "testKey2.0": "testVal2" },
    { "testKey2.1": "testVal2" },
    { "testKey2.2": "testVal2" },
  ] 
};

var x3 = {
  "items": [
    { "testKey3.0": "testVal3" },
    { "testKey3.1": "testVal3" }
  ] 
};

function combineArrays(...arrays) {

  var tempArray = [];
  for (let index in arrays) {
    let currentArray = arrays[index];
    for (let innerArrayIndex in currentArray) {
      tempArray.push(currentArray[innerArrayIndex]);
    }
  } 
  
  return tempArray;
}

var result = combineArrays(x1.items, x2.items, x3.items);
console.log(result);

The solutions using a spread operator do not take into consideration that all the objects will be cloned using a shallow copy. Have a look.

Related