jenna_3108
jenna_3108

Reputation: 435

Update object in array from another object in array

In my code I have two arrays, first one contains multiple objects. While the second one is to store serialized form data (mapped to JSON). So both arrays are having identical keys.

What I want to achieve is to update the values of an object in the original array based on the values of an object in new array dynamically, by ID in the object.

Found some examples online but hard to get them to work. Because most of them are showing either one level of objects but I'm working on complex nested objects in the array.

var products = [
    {
        Id: 1,
        Name: 'Product1',
        Attributes: {
            Storage: 'Normal',
            Size: 'Small'
        }
    },
    {
        Id: 2,
        Name: 'Product2',
        Attributes: {
            Storage: 'Normal',
            Size: 'Small'
        }
    }
];

var newData = [
    {
        Id: 2,
        Name: 'French Fries'
    },
    {
        Id: 1,
        Attributes: {
            Size: 'Medium'
        }
    }
];

The expected outcome is the products array now updated with the values from the second array.

Output:
[
    {
        Id: 1,
        Name: 'Product1',
        Attributes: {
            Storage: 'Normal',
            Size: 'Medium'
        }
    },
    {
        Id: 2,
        Name: 'French Fries',
        Attributes: {
            Storage: 'Normal',
            Size: 'Small'
        }
    }
]

Upvotes: 2

Views: 1379

Answers (2)

Nina Scholz
Nina Scholz

Reputation: 386560

You could take a Map for the update items and iterate products.

If an item is found for an update, take a recursive approach and iterate the entries and check if the value is an object, then iterate the nested properties.

If no nested object found update the property.

This works for arrays as well.

function update(target, source) {
    Object.entries(source).forEach(([key, value]) => {
        if (value && typeof value === 'object') {
            update(target[key] = target[key] || (Array.isArray(value) ? [] : {}), value);
        } else if (target[key] !== value) {
            target[key] = value;
        }
    });
}

var products = [{ Id: 1, Name: 'Product1', Attributes: { Storage: 'Normal', Size: 'Small' } }, { Id: 2, Name: 'Product2', Attributes: { Storage: 'Normal', Size: 'Small' } }],
    newData = [{ Id: 2, Name: 'French Fries' }, { Id: 1, Attributes: { Size: 'Medium' } }],
    map = new Map(newData.map(o => [o.Id, o]));

products.forEach(o => map.has(o.Id) && update(o, map.get(o.Id)));

console.log(products);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 3

Maheer Ali
Maheer Ali

Reputation: 36564

You can create a function which combine nested objects. And then use map() and find() to create combined array of objects.

var products = [
    {
        Id: 1,
        Name: 'Product1',
        Attributes: {
            Storage: 'Normal',
            Size: 'Small'
        }
    },
    {
        Id: 2,
        Name: 'Product2',
        Attributes: {
            Storage: 'Normal',
            Size: 'Small'
        }
    }
];

var newData = [
    {
        Id: 2,
        Name: 'French Fries'
    },
    {
        Id: 1,
        Attributes: {
            Size: 'Medium'
        }
    }
];

const haveNested = obj => Object.values(obj).some(x => typeof x === "object");

function combine(obj1,obj2){

  if(!haveNested(obj1)) return ({...obj1,...obj2})
  let res = obj1
  for(let key in obj1){
    if(typeof obj1[key] === "object"){
      res[key] = combine(obj1[key],obj2[key]);
    }
    else if(obj2[key]) res[key] = obj2[key]
  }
  return res;
}

const result = products.map(x => {
  let temp = newData.find(a => a.Id === x.Id);
  return temp ? combine(x,temp) : x;
})

console.log(result)

Upvotes: 0

Related Questions