1

Given this array of objects:

[
    {"Code": 101, "Description": "Invoice"},
    {"Code": 102, "Description": "Credit"},
    {"Code": 103, "Description": "Credit"},
    {"Code": 111, "Description": "Invoice"},
    {"Code": 112, "Description": "Credit"},
    {"Code": 113, "Description": "Credit"},
    {"Code": 182, "Description": "Backup"}
]

I need to combine the objects that have matching Descriptions into one single object with an array of Codes, like so:

[
    {"Code": [101,111], "Description": "Invoice"},
    {"Code": [102,103,112,113], "Description": "Credit"},
    {"Code": [182], "Description": "Backup"}
]

I have tried with the groupBy() helper function below (the vanilla JS one) and it is not working because I get an Object with a single undefined property with the original array as it's value:

var groupBy = function (arr, criteria) {
    return arr.reduce(function (obj, item) {

        // Check if the criteria is a function to run on the item or a property of it
        var key = typeof criteria === 'function' ? criteria(item) : item[criteria];

        // If the key doesn't exist yet, create it
        if (!obj.hasOwnProperty(key)) {
            obj[key] = [];
        }

        // Push the value to the object
        obj[key].push(item);

        // Return the object to the next item in the loop
        return obj;

    }, {});
};

var array = [
    {"Code": 101, "Description": "Invoice"},
    {"Code": 102, "Description": "Credit"},
    {"Code": 103, "Description": "Credit"},
    {"Code": 111, "Description": "Invoice"},
    {"Code": 112, "Description": "Credit"},
    {"Code": 113, "Description": "Credit"},
    {"Code": 182, "Description": "Backup"}
];

var groupDescription = groupBy(array, array.forEach(document => document.Description));

console.log(groupDescription);

What would be the best way to achieve my expected output?

Robin Mackenzie
  • 18,801
  • 7
  • 38
  • 56

1 Answers1

1

Your expected output is an Array, but the reduce logic in your groupBy function takes an Object as it's initial value. It's not obvious why you want to run the criteria function for each item in the input array, as Description is a String, not an Array.

A simpler approach (may, or may not, be the best):

  • get the unique set of Descriptions as an array
  • iterate that array and for each Description, filter the original array and get the Codes

See below:

var arr = [
  {"Code": 101, "Description": "Invoice"},
  {"Code": 102, "Description": "Credit"},
  {"Code": 103, "Description": "Credit"},
  {"Code": 111, "Description": "Invoice"},
  {"Code": 112, "Description": "Credit"},
  {"Code": 113, "Description": "Credit"},
  {"Code": 182, "Description": "Backup"}
];

// expected output
//[
//  {"Code": [101,111], "Description": "Invoice"},
//  {"Code": [102,103,112,113], "Description": "Credit"},
//  {"Code": [182], "Description": "Backup"}
//]

var grouped = Array
  .from(new Set(arr.map(o => o.Description))) // get unique Descriptions as array
  .map(d => { // iterate that array
    return {
      "Code": arr
        .filter(o => o.Description === d) 
        .map(o => o.Code), // return just the Code for original array items matching this unique Description
      "Description": d  // the Description
    }
  });
  
console.log(grouped);
Robin Mackenzie
  • 18,801
  • 7
  • 38
  • 56
  • Amazing! Now this works exactly as I need it. Thanks a lot, Robin. And thanks for clarifying to the Mods that this is a different kind of question. – Martin Scola Mar 12 '21 at 14:55