Tom
Tom

Reputation: 21

Combining two arrays of objects where id keys match in JavaScript

I have a JavaScript question that involves combining pieces of different arrays.

I have an array of objects below:

"people": [
        {
            "city": "SF",
            "email": "[email protected]",
            "firstName": "Bob",
            "id": "1",
            "lastName": "Smith"
        },
        {
            "city": "Boston",
            "email": "[email protected]",
            "firstName": "Bill",
            "id": "2",
            "lastName": "Anderson"
        },
        {
            "city": "Toronto",
            "email": "[email protected]",
            "firstName": "Ann",
            "id": "3",
            "lastName": "Kline",
        }
]

I also have an array of tag arrays with an id object in each:

[["tag 1", "tag 2", {"id": "1"}], ["tag 8", "tag 2", {"id": "3"}]]

The expected output would be:

"people": [
        {
            "city": "SF",
            "email": "[email protected]",
            "firstName": "Bob",
            "id": "1",
            "lastName": "Smith",
            "tags": ["tag 1", "tag 2"]
        },
        {
            "city": "Boston",
            "email": "[email protected]",
            "firstName": "Bill",
            "id": "2",
            "lastName": "Anderson"
        },
        {
            "city": "Toronto",
            "email": "[email protected]",
            "firstName": "Ann",
            "id": "3",
            "lastName": "Kline",
            "tags": ["tag 8", "tag 2"]
        }
]

Is there a way to add the tags to the objects above where the "id"'s match without doing a nested loop. I have tried a couple ways but have failed.

Upvotes: 0

Views: 668

Answers (2)

adiga
adiga

Reputation: 35222

You could create a Map object which maps each id with the tags. Loop through the people array and add a property if the Map has the current id as key

const people=[{city:"SF",email:"[email protected]",firstName:"Bob",id:"1",lastName:"Smith"},{city:"Boston",email:"[email protected]",firstName:"Bill",id:"2",lastName:"Anderson"},{city:"Toronto",email:"[email protected]",firstName:"Ann",id:"3",lastName:"Kline",}],
      tags = [["tag 1", "tag 2", {"id": "1"}], ["tag 8", "tag 2", {"id": "3"}]]
      
const map = new Map( tags.map(t => [t.pop().id, t]) )

for (const p of people)
  if (map.has(p.id))
    p.tags = map.get(p.id)

console.log(people)

You can also create a version which doesn't mutate the arrays

const map = new Map;

for (const t of tags)
    map.set( t[t.length - 1].id, t.slice(0, -1) )

const output = people.map(p => ({
    ...p,
    tags: map.get(p.id) ?? []
}));

Upvotes: 1

Alberto
Alberto

Reputation: 12909

Maybe something like this:

let input = [
        {
            "city": "SF",
            "email": "[email protected]",
            "firstName": "Bob",
            "id": "1",
            "lastName": "Smith"
        },
        {
            "city": "Boston",
            "email": "[email protected]",
            "firstName": "Bill",
            "id": "2",
            "lastName": "Anderson"
        },
        {
            "city": "Toronto",
            "email": "[email protected]",
            "firstName": "Ann",
            "id": "3",
            "lastName": "Kline",
        }
]
let toAdd = [["tag 1", "tag 2", {"id": "1"}], ["tag 8", "tag 2", {"id": "3"}]].reduce((acc, el) => {
  // build a object with id -> list of tags
  acc[el.find(o => typeof o === 'object' && o !== null).id] = el.filter(o => !(typeof o === 'object' && o !== null))
  return acc;
}, {})
console.log(input.map(el => {
   // add the tags to el if tags exists
   el.tags = toAdd[el.id] || [];
   return el
}))

this is O(n^2).. if you are certain that your array has always that object at the end, you can have a O(n) with

let input = [
        {
            "city": "SF",
            "email": "[email protected]",
            "firstName": "Bob",
            "id": "1",
            "lastName": "Smith"
        },
        {
            "city": "Boston",
            "email": "[email protected]",
            "firstName": "Bill",
            "id": "2",
            "lastName": "Anderson"
        },
        {
            "city": "Toronto",
            "email": "[email protected]",
            "firstName": "Ann",
            "id": "3",
            "lastName": "Kline",
        }
]
let toAdd = [["tag 1", "tag 2", {"id": "1"}], ["tag 8", "tag 2", {"id": "3"}]].reduce((acc, el) => {
  // build a object with id -> list of tags
  acc[el.pop().id] = el
  return acc;
}, {})
console.log(input.map(el => {
   // add the tags to el if tags exists
   el.tags = toAdd[el.id] || [];
   return el
}))

Upvotes: 0

Related Questions