1

I am attempting to merge two objects that both, have some same keys/associated arrays and different keys. The associated arrays are also of different length. To give you a better picture, I have included some code below that shows the two objects I am trying to merge (filters1 and filters2) and the desired outcome after merging (combinedFilters1).

I've tried methods like Object.assign() or for loops (which I have also included below), but I can't seem to get the result I outlined below (combinedFilters1). Any suggestions? All help is greatly appreciated. Thank you in advance.

// first object
this.filters1 = {
      "categories":
      [
        {
          "categoryName":"Video",
          "categoryAttributes":
          [
            {
              "name":"aspectRatio",
              "values":["4:3", "16:15"]
            },
            {
              "name":"Bit Rate",
              "values":["256kbps", "512kbps"]
            }
          ]
        },
        {
          "categoryName":"Audio",
          "categoryAttributes":
          [
            {
              "name":"Speaker",
              "values":["In-built", "External Connector"]
            },
            {
              "name":"Bit Rate",
              "values":["256kbps", "376kbps", "512kbps"]
            }
          ]
        }
      ]
    };

// second object
this.filters2 = {
  "categories":
  [
    {
      "categoryName":"Video",
      "categoryAttributes":
      [
        {
          "name":"aspectRatio",
          "values":["4:3", "16:15", "16:9"]
        },
        {
          "name":"Bit Rate",
          "values":["256kbps", "512kbps", "1024kbps"]
        }
      ]
    },
    {
      "categoryName":"Audio",
      "categoryAttributes":
      [
        {
          "name":"Speaker",
          "values":["In-built", "External Connector"]
        },
        {
          "name":"Bit Rate",
          "values":["256kbps", "376kbps", "512kbps", "1024kbps"]
        }
      ]
    },
    {
      "categoryName":"OS",
      "categoryAttributes":
      [
        {
          "name":"Android",
          "values":["Lolipop", "Marshmello"]
        },
        {
          "name":"Apple",
          "values":["IOS 5", "IOS 6"]
        }
      ]
    }
  ]
};

// desired outcome
this.combinedFilters1 = {
  "categories":
  [
    {
      "categoryName":"Video",
      "categoryAttributes":
      [
        {
          "name":"aspectRatio",
          "values":["4:3", "16:15", "16:9"]
        },
        {
          "name":"Bit Rate",
          "values":["256kbps", "512kbps", "1024kbps"]
        }
      ]
    },
    {
      "categoryName":"Audio",
      "categoryAttributes":
      [
        {
          "name":"Speaker",
          "values":["In-built", "External Connector"]
        },
        {
          "name":"Bit Rate",
          "values":["256kbps", "376kbps", "512kbps", "1024kbps"]
        }
      ]
    },
    {
      "categoryName":"OS",
      "categoryAttributes":
      [
        {
          "name":"Android",
          "values":["Lolipop", "Marshmello"]
        },
        {
          "name":"Apple",
          "values":["IOS 5", "IOS 6"]
        }
      ]
    }
  ]
};

// attempted this loop
for (let i = 0, j = 0; i < this.filters1.categories.length,
      j < this.filters2.categories.length; i++, j++) {
      // console.log(this.filters1.categories[i].categoryName + " " + this.filters2.categories[j].categoryName);

  if(this.filters1.categories.includes(this.filters2.categories[j].categoryName)) {
    continue;
  } else {
    this.combinedFilters1.categories.push({"categoryName": this.filters2.categories[j].categoryName});
  }

  console.log(this.combinedFilters1);

  for (let k = 0, l = 0; k < this.filters1.categories[i].categoryAttributes.length,
    l < this.filters2.categories[j].categoryAttributes.length; k++, l++) {
    console.log(this.filters1.categories[i].categoryAttributes[k]);
    console.log(this.filters2.categories[j].categoryAttributes[l]);

    if(this.filters1.categories.includes({"categoryAttributes" : [{"name": this.filters2.categories[j].categoryAttributes[l].name}]})) {
      continue;
    } else {
      this.combinedFilters1.categories[i]["categoryAttributes"] = [{"name": this.filters2.categories[j].categoryAttributes[l].name}];
    }
  }
}

The first part of the for loop adds the unique categories (video, audio, OS), but the second part of the for loop doesn't add the unique attributes and I need to do the same for the values array. All help is greatly appreciated, thank you.

2 Answers 2

0

You could take a dynamic function which takes two arrays, and array of entries with key and value names, a function processing the final level and an index.

function merge(a, b, entries, final, index = 0) {
    var [k, v] = entries[index];
    return b.reduce((r, o) => {
        var temp = r.find(q => o[k] === q[k]);
        if (!temp) {
            r.push(o);
        } else if (index + 1 === entries.length) {
            temp[v] = final(temp[v], o[v]);
        } else {
            merge(temp[v], o[v], entries, final, index + 1);
        }                
        return r;
    }, a);
}

var filters1 = { categories: [{ categoryName: "Video", categoryAttributes: [{ name: "aspectRatio", values: ["4:3", "16:15"] }, { name: "Bit Rate", values: ["256kbps", "512kbps"] }] }, { categoryName: "Audio", categoryAttributes: [{ name: "Speaker", values: ["In-built", "External Connector"] }, { name: "Bit Rate", values: ["256kbps", "376kbps", "512kbps"] }] }] },
    filters2 = { categories: [{ categoryName: "Video", categoryAttributes: [{ name: "aspectRatio", values: ["4:3", "16:15", "16:9"] }, { name: "Bit Rate", values: ["256kbps", "512kbps", "1024kbps"] }] }, { categoryName: "Audio", categoryAttributes: [{ name: "Speaker", values: ["In-built", "External Connector"] }, { name: "Bit Rate", values: ["256kbps", "376kbps", "512kbps", "1024kbps"] }] }, { categoryName: "OS", categoryAttributes: [{ name: "Android", values: ["Lolipop", "Marshmello"] }, { name: "Apple", values: ["IOS 5", "IOS 6"] }] }] },
    parts = [
        [undefined, 'categories'],
        ['categoryName', 'categoryAttributes'],
        ['name', 'values']
    ],
    fn = (a, b) => [...new Set([...a, ...b])],
    result = merge([filters1], [filters2], parts, fn);

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

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

1 Comment

This is perfect, you're a life saver, thank you! I used it in 2 iterations because I had to merge a third object.
0

If you just want to combine the two arrays into one:

this.combinedFilters1 = {categories: [...this.filters1.categories, ...this.filters2.categories]}

But in your 'desired outcome' section, it looks like you're overwriting any array elements of filters1 that have the same categoryName as the elements of filters2. If that's what you're trying to do first I'd structure your data so that categories is an object rather than an array, like so

this.filters1 = {
"categories": {
  "Video": {
    "categoryAttributes": [
        {
          "name":"aspectRatio",
          "values":["4:3", "16:15"]
        },
        {
          "name":"Bit Rate",
          "values":["256kbps", "512kbps"]
        }
      ]
    },
    "Audio": {/*data here*/}
  }
}

then do

this.combinedFilters1 = {categories: {...this.filters1.categories, ...this.filters2.categories}}

That will give you the behavior of the second overwriting the first.

If you really can't change the data structure, then you'll have to do some inefficient looping.

this.combinedFilters1 = {...this.filters1};
for (const cat1 of this.combinedFilters1.categories) {
  this.combinedFilters1.categories = this.filters2.categories.reduce((accumulator, cat2) => {
    accumulator.push(cat1.categoryName === cat2.categoryName ? cat2 : cat1)
    return accumulator;
  }, [])
}

Note: this code only works if filters2 is longer than filters1;

2 Comments

I cannot change the structure of the original objects. I tried the loop you provided, but it gives the following output:
{"categories":[{"categoryName":"Audio","categoryAttributes":[{"name":"Speaker","values":["In-built","External Connector"]},{"name":"Bit Rate","values":["256kbps","376kbps","512kbps"]}]},{"categoryName":"Audio","categoryAttributes":[{"name":"Speaker","values":["In-built","External Connector"]},{"name":"Bit Rate","values":["256kbps","376kbps","512kbps","1024kbps"]}]},{"categoryName":"Audio","categoryAttributes":[{"name":"Speaker","values":["In-built","External Connector"]},{"name":"Bit Rate","values":["256kbps","376kbps","512kbps"]}]}]}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.