skip to Main Content

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 ?

4

Answers


  1. 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);
    Login or Signup to reply.
  2. You can reduce your array to grouped sets with unique category values, then reduce again to convert sets to arrays. The final result can be converted to any string you want:

    const grouped = categories.reduce((r, {category, options}) => (options.forEach(o => (r[category]??=new Set).add(o)), r), {});
    const result = Object.keys(grouped).reduce((r, key) => (r[key] = [...grouped[key]], r), {});
    console.log(result);
    <script>
    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"]
        }
    ];
    </script>
    Login or Signup to reply.
  3. const categories = [{
      category: "mammal",
      animal: ["horse", "cow"],
    }, {
      category: "mammal",
      options: ["pig", "cow"],
    }, {
      category: "gender",
      options: ["male"],
    }, {
      category: "mammal",
      options: ["cow"],
    }, {
      category: "mammal",
      options: ["pig"],
    }, {
      category: "gender",
      options: ["female"],
    }];
    
    const result = {};
    
    for (let i = 0; i < categories.length; i++) {
        const currentCategory = categories[i].category;
        const currentOptions = categories[i].options || categories[i].animal || [];
    
        if (!result[currentCategory]) {
            // If the category doesn't exist in the result object, create an array for it
            result[currentCategory] = [];
        }
    
        for (let j = 0; j < currentOptions.length; j++) {
            const currentOption = currentOptions[j];
            if (result[currentCategory].indexOf(currentOption) === -1) {
                // If the option doesn't exist in the category, add it
                result[currentCategory].push(currentOption);
            }
        }
    }
    
    console.log(result);

    This code initializes an empty object (result) to store the cleaned-up data. It then iterates through the original array, checks whether the category already exists in the result object, and adds the options to the corresponding category while ensuring no duplicates are added.

    The resulting result object will have categories as keys, each associated with an array of unique options.

    Login or Signup to reply.
  4. The next provided solution implements a reduce based approach as well. It differs in how the uniqueness of the final category arrays is achieved. One does always either initially create or reassign an instantly computed array of unique string values by concatenating the currently processed options array to the initially empty, but later available, array which is/got grouped by a category specific key. The computation is done by creating a Set instance from the concatenated/merged array and immediately spreading it back into an array value.

    const optionsByCategoryLookup = categories
      .reduce((result, item) => {
    
        // - destructure the currently processed array item.
        const { category, options } = item;
    
        // - aggregate the final `result`, an object, used as lookup,
        //   which is going to feature key-value pairs (entries), where
        //   the key is the `category` value, and the value is ...
        result[category] = [
          // ... the ever newly calculated array 
          //     of unique `options` string-values.
          ...new Set(
            (result[category] ?? []).concat(options)
          )
        ];
        return result;  
    
      }, {});
    
    console.log({ optionsByCategoryLookup });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
      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"],
      }];
    </script>

    And in case one want to re-use the reduce functionality, one does implement and provide it as e.g. function statement …

    function aggregateCategoryBasedOptionsLookup(
      result, { category, options }
    ) {
      result[category] = [
        ...new Set(
          (result[category] ?? []).concat(options)
        )
      ];
      return result;  
    }
    
    const optionsByCategoryLookup = categories
      .reduce(aggregateCategoryBasedOptionsLookup, {});
    
    console.log({ optionsByCategoryLookup });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
      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"],
      }];
    </script>

    Edit

    And since the OP in the beginning was not really clear about the finally expected result one can built at the above approach.

    One anyhow has to undergo the process of unifying/normalizing the provided data-structure. Thus the lookup object would serve as an intermediate result from which one does compute the final string based result (which once again does proof the advantage of small re-usable implemented functions) …

    function aggregateCategoryBasedOptionsLookup(
      result, { category, options }
    ) {
      result[category] = [
        ...new Set(
          (result[category] ?? []).concat(options)
        )
      ];
      return result;  
    }
    function createStringBasedCategoryGraph(lookup) {
      // mammal>horse;cow;pig/gender>male;female/
      return Object
        .entries(lookup)
        .map(([key, value]) =>
          `${ key }>${ value.join(';') }/`
        )
        .join('');
    }
    
    const categoryGraph = createStringBasedCategoryGraph(
      categories
        .reduce(aggregateCategoryBasedOptionsLookup, {})
    );
    console.log({ categoryGraph });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
      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"],
      }];
    </script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search