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!

1
  • Does the order of the objects in the output array matter? Commented Mar 8, 2021 at 2:43

4 Answers 4

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

Sign up to request clarification or add additional context in comments.

Comments

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

1 Comment

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.