Search code examples
javascriptarraysobject

How to compare items in a multi level object


I have an object that is auto-generated that I need to obtain the data from, but first I need to clean it up so that there are no more duplicates and any additions are concatenated.

I currently have something like this example which is made up for illustrative purposes

const categories = [{
  category: "mammal",
  options: ["horse", "cow"],
}, {
  category: "mammal",
  options: ["pig", "cow"],
}, {
  category: "gender",
  options: ["male"],
}, {
  category: "mammal",
  options: ["cow"],
}, {
  category: "mammal",
  options: ["pig"],
}, {
  category: "gender",
  options: ["female"],
}];

What I am aiming for is to convert it into something like this:

mammal>horse;cow;pig/gender>male;female/

I have been successful by looping through the current array and comparing the properties of the objects, but I have failed to get any traction on looping through the options and appending them if they are not duplicates.

const newArr = [];

for (let i = 0; i < categories.length; i++) {
    categoryIsInArray = newCat.indexOf(categories[i].category) !== -1;
    if (categoryIsInArray === true) {
         // do something with the options
    }
    else {
        newArr.push(categories[i].category)
    }
}

This results in a rather cut-down array:

["mammal","gender"]

I assume that I should be able to loop through the options and append them to their appropriate category if they don't already exist in that category. So I attempted a similar approach.

const newArr = [];

for (let i = 0; i < categories.length; i++) {
    categoryIsInArray = newCat.indexOf(categories[i].category) !== -1;
    if (categoryIsInArray === true) {
        for (let j = 0; j < categories[i].options.length; j++) {
            optionIsInArray = newCat.indexOf(categories[i].options[j]) !== -1;
            if(optionIsInArray === false) {
                newCat.push(categories[i].options)
            }
        }
    }
    else {
        newArr.push(categories[i].category)
    }
}

but that has just mashed up everything and is not what I want at all

 [
    'mammal',
    [
        'horse',
        'cow'
    ],
    [
        'horse',
        'cow'
    ],
    'gender',
    [
        'cow'
    ],
    [
        'horse'
    ],
    [
        'female'
    ]
]

How do I adjust this to get what I'm after ?


Solution

  • You could group by category and use a Set for unique options.

    const
        categories = [{ category: "mammal", options: ["chicken", "cow"] }, { category: "mammal", options: ["pig", "cow"] }, { category: "gender", options: ["male"] }, { category: "mammal", options: ["cow"] }, { category: "mammal", options: ["pig"] }, { category: "gender", options: ["female"] }],
        result = Object
            .entries(categories.reduce((r, { category, options }) => {
                options.forEach(Set.prototype.add, r[category] ??= new Set);
                return r;
            }, {}))
            .map(([key, values]) => `${key}>${[...values].join(';')}/`)
            .join('');
    
    console.log(result);