0

I have two arrays containing nested objects. I'd like to merge those and get a unique array

I have these two arrays

// ARRAY 1
let variants = [
  {color: "Red", sizes: "Small", material: "Cotton", price: "$100", ...},
  {color: "Red", sizes: "Large", material: "Cotton", price: "$120", ...},
  {color: "Blue", sizes: "Small", material: "Cotton", price: "$150", ...},
  {color: "Blue", sizes: "Large", material: "Cotton", price: "$180", ...},
]
// ARRAY 2
let newVariants = [
  {color: "Red", sizes: "Small", material: "Cotton"}, // this one is already exist in ARRAY 1
  {color: "Red", sizes: "Large", material: "Cotton"}, // this one is already exist in ARRAY 1
  {color: "Blue", sizes: "Small", material: "Cotton"}, // this one is already exist in ARRAY 1
  {color: "Blue", sizes: "Large", material: "Wool"}, // this one is new object
  {color: "Green", sizes: "Large", material: "Cotton"}, // this one is new object
]

and I want this

[
  {color: "Red", sizes: "Small", material: "Cotton", price: "$100"},
  {color: "Red", sizes: "Large", material: "Cotton", price: "$120"},
  {color: "Blue", sizes: "Small", material: "Cotton", price: "$150"},
  {color: "Blue", sizes: "Large", material: "Cotton", price: "$180"},
  {color: "Blue", sizes: "Large", material: "Wool", price: null, ...},
  {color: "Green", sizes: "Large", material: "Cotton", price: null, ...}
]

Note: ARRAY 1's value will always supersede on ARRAY 2

Thanks!

Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
Muhammad Taseen
  • 519
  • 7
  • 22

4 Answers4

2

ARRAY 1's value will always supersede on ARRAY 2

You can filter by using .filter and .some like below:

let variants = [ {color: "Red", sizes: "Small", material: "Cotton", price: "$100",}, {color: "Red", sizes: "Large", material: "Cotton", price: "$120",}, {color: "Blue", sizes: "Small", material: "Cotton", price: "$150",}, {color: "Blue", sizes: "Large", material: "Cotton", price: "$180",}, ];
let newVariants = [ {color: "Red", sizes: "Small", material: "Cotton"}, {color: "Red", sizes: "Large", material: "Cotton"}, {color: "Blue", sizes: "Small", material: "Cotton"}, {color: "Blue", sizes: "Large", material: "Wool"}, {color: "Green", sizes: "Large", material: "Cotton"}, ];

const isEqual = (p1, p2) => p1.color == p2.color && p1.sizes == p2.sizes && p1.material == p2.material;
const filteredExtraVariants = newVariants.filter(p1 => !variants.some(p2 => isEqual(p1, p2)));
const extraVariants = filteredExtraVariants.map(r => 
{
  r.price = null;
  return r;
});

const result = variants.concat(extraVariants);
console.log(result);
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
  • 1
    Hey Phong, Thanks for your help, I really appreciated it, I accept Nick's answer coz I don't want to use 2nd object props in the filter function, these props may be different each time, but I upvote for both answers from you. Thanks again – Muhammad Taseen Mar 09 '21 at 17:11
2

I would merge the two arrays, with array2 coming before array1. Then you can use .reduce() to build an object (ie: a Map) keyed by a concatenated string of the values you want to merge by. By having array1 second in your merged arrays, the key/values from the objects within that will overwrite those from array2 (and so array1's objects will take precedence):

let variants = [ {color: "Red", sizes: "Small", material: "Cotton", price: "$100",}, {color: "Red", sizes: "Large", material: "Cotton", price: "$120",}, {color: "Blue", sizes: "Small", material: "Cotton", price: "$150",}, {color: "Blue", sizes: "Large", material: "Cotton", price: "$180",}, ];
let newVariants = [ {color: "Red", sizes: "Small", material: "Cotton"}, {color: "Red", sizes: "Large", material: "Cotton"}, {color: "Blue", sizes: "Small", material: "Cotton"}, {color: "Blue", sizes: "Large", material: "Wool"}, {color: "Green", sizes: "Large", material: "Cotton"}, ];

const res = Array.from([...newVariants, ...variants].reduce((acc, {price=null, ...rest}) => {
  const key = Object.entries(rest).join("-"); // get a key based on the values (excluding the price)
  return acc.set(key, {...rest, price});
}, new Map).values());

console.log(res);

Otherwise, if order of the output array matters, you can reverse the merge order of the arrays, and use a check to see whether or not the value has already been set before adding it as a value in your object:

let variants = [ {color: "Red", sizes: "Small", material: "Cotton", price: "$100",}, {color: "Red", sizes: "Large", material: "Cotton", price: "$120",}, {color: "Blue", sizes: "Small", material: "Cotton", price: "$150",}, {color: "Blue", sizes: "Large", material: "Cotton", price: "$180",}, ];
let newVariants = [ {color: "Red", sizes: "Small", material: "Cotton"}, {color: "Red", sizes: "Large", material: "Cotton"}, {color: "Blue", sizes: "Small", material: "Cotton"}, {color: "Blue", sizes: "Large", material: "Wool"}, {color: "Green", sizes: "Large", material: "Cotton"}, ];

const res = Array.from([...variants, ...newVariants].reduce((acc, {price=null, ...rest}) => {
  const key = Object.entries(rest).join("-"); // get a key based on the values (excluding the price)
  return acc.set(key, acc.get(key) || {...rest, price});
}, new Map).values());

console.log(res);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
1

You can do:

const variants = [{ color: "Red", sizes: "Small", material: "Cotton", price: "$100" }, { color: "Red", sizes: "Large", material: "Cotton", price: "$120" }, { color: "Blue", sizes: "Small", material: "Cotton", price: "$150" }, { color: "Blue", sizes: "Large", material: "Cotton", price: "$180" }]
const newVariants = [{ color: "Red", sizes: "Small", material: "Cotton" }, { color: "Red", sizes: "Large", material: "Cotton" }, { color: "Blue", sizes: "Small", material: "Cotton" }, { color: "Blue", sizes: "Large", material: "Wool" }, { color: "Green", sizes: "Large", material: "Cotton" }]

const result = Object.values(
  [...newVariants, ...variants].reduce((a, { color, sizes, material, price = null }) =>
    (a[`${color}-${sizes}-${material}`] = { color, sizes, material, price }, a), {})
)

console.log(result)
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46
1

Another way is using .reduce like below:

const variants = [{ color: "Red", sizes: "Small", material: "Cotton", price: "$100" }, { color: "Red", sizes: "Large", material: "Cotton", price: "$120" }, { color: "Blue", sizes: "Small", material: "Cotton", price: "$150" }, { color: "Blue", sizes: "Large", material: "Cotton", price: "$180" }]
const newVariants = [{ color: "Red", sizes: "Small", material: "Cotton" }, { color: "Red", sizes: "Large", material: "Cotton" }, { color: "Blue", sizes: "Small", material: "Cotton" }, { color: "Blue", sizes: "Large", material: "Wool" }, { color: "Green", sizes: "Large", material: "Cotton" }]

const result = [...variants, ...newVariants].reduce((acc, {color, sizes, material, price = null}) => {
   const key = `${color}-${sizes}-${material}`;
   acc[key] ??= {color, sizes, material, price};
   return acc;
  }, {});

console.log(Object.values(result))
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56