JordanBarber
JordanBarber

Reputation: 2101

Merge arrays from different objects with same key

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/

Upvotes: 2

Views: 357

Answers (5)

customcommander
customcommander

Reputation: 18901

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 ?? []), [])

Upvotes: 2

Botea Bogdan
Botea Bogdan

Reputation: 191

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.

Upvotes: 0

malarres
malarres

Reputation: 2946

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

Upvotes: 1

lejlun
lejlun

Reputation: 4419

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);

Upvotes: 1

rustyBucketBay
rustyBucketBay

Reputation: 4561

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.

Upvotes: 0

Related Questions