0

I have an array of 100 objects with category and subcategory properties and I want to create a new array with the name of each category and the number(count) of subcategories for that category?

Example of array:

[{
    ticketId: 1,
    Category: "Driver",
    CategoryID: 29,
    SubCategory: "Monitor",
    SubCategoryID: 31
}, {
    ticketId: 2,
    Category: "Driver",
    CategoryID: 29,
    SubCategory: "Monitor",
    SubCategoryID: 31
}, {
    ticketId: 3,
    Category: "Hardware",
    CategoryID: 11,
    SubCategory: "Monitor",
    SubCategoryID: 32
}, {
    ticketId: 4,
    Category: "Hardware",
    CategoryID: 11,
    SubCategory: "phone",
    SubCategoryID: 13
}];

Example of list of categories:

[{
    "ID": 1,
    "ParentID": 0,
    "Name": "Printing"
}, {
    "ID": 2,
    "ParentID": 1,
    "Name": "Toner"
}, {
    "ID": 3,
    "ParentID": 1,
    "Name": "Power"
}, {
    "ID": 4,
    "ParentID": 1,
    "Name": "Paper Jam"
}, {
    "ID": 5,
    "ParentID": 0,
    "Name": "Office Applications"
}]

Parent ID of 0 being the Category and any other number being the SubCategory

joaumg
  • 1,238
  • 1
  • 12
  • 27
Rethabile
  • 325
  • 3
  • 22
  • 2
    You can use some kind of map and reduce – Cyberdelphos Jan 11 '16 at 21:48
  • are the categories, nested within the JSON object or a separate array? – Jordan Davis Jan 11 '16 at 21:48
  • The categories are in a different array – Rethabile Jan 11 '16 at 21:49
  • With the example I used above I would like to say Category: Hardware 2 SubCategory: Phone 1 – Rethabile Jan 11 '16 at 21:52
  • how does the two array map together? – Nina Scholz Jan 11 '16 at 21:52
  • @Rethabile Is there a reason why their in separate arrays? It would be easier and make way more since that the category property would be in array of JSON objects those being the sub-categories. – Jordan Davis Jan 11 '16 at 21:52
  • The second array is a list of all the categories and subcategories available. The tickets might not be using all the categories and subcategories. I included it to show what I am working with. Should I remove the second array? – Rethabile Jan 11 '16 at 21:55
  • Here's a really great video (and also short) to introduce you on mapping and reducing: https://egghead.io/lessons/javascript-introducing-reduce-reducing-an-array-into-an-object – Cyberdelphos Jan 11 '16 at 21:56
  • please add an example of the wanted result. – Nina Scholz Jan 11 '16 at 21:57
  • Yea I think you should. Yea you should list the subcategories under each category, so create an array of JSON objects then on each `category: [ ] `open another array and then start listing all the subcategories for that specific category. Fixing the formatting issue will allow you to use a very simple `for of` and `for in` object loops and would be efficient and get maximum performance. – Jordan Davis Jan 11 '16 at 21:58
  • I'll post the proper format below. – Jordan Davis Jan 11 '16 at 22:06
  • The arrays are coming from an API that I don't have access too. Would you suggest I modify it once I receive it? – Rethabile Jan 11 '16 at 22:07

5 Answers5

2

Maybe this works for you. It utilizes Array.prototype.reduce()

var data = [{ ticketId: 1, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 2, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 3, Category: "Hardware", CategoryID: 11, SubCategory: "Monitor", SubCategoryID: 32 }, { ticketId: 4, Category: "Hardware", CategoryID: 11, SubCategory: "phone", SubCategoryID: 13 }],
    count = data.reduce(function (r, a) {
        r[a.Category] = r[a.Category] || {};
        r[a.Category][a.SubCategory] = (r[a.Category][a.SubCategory] || 0) + 1;
        return r;
    }, {});

document.write('<pre>' + JSON.stringify(count, 0, 4) + '</pre>');

Just only the counts in Category:

var data = [{ ticketId: 1, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 2, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 3, Category: "Hardware", CategoryID: 11, SubCategory: "Monitor", SubCategoryID: 32 }, { ticketId: 4, Category: "Hardware", CategoryID: 11, SubCategory: "phone", SubCategoryID: 13 }],
    count = data.reduce(function (r, a) {
        r[a.Category] = (r[a.Category] || 0) + 1;
        return r;
    }, {});

document.write('<pre>' + JSON.stringify(count, 0, 4) + '</pre>');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thank you for taking the time to respond. Would you have any recommendations on readings for Array.prototype.reduce()? – Rethabile Jan 11 '16 at 22:06
  • first of all a look to the language specification is a good thing, either the ecma standard or at mdn, like the link above. then i would seach this site for javascript and reduce (without and). but the best way to deal with it is to try and test own ideas with it. – Nina Scholz Jan 11 '16 at 22:12
  • I did not realize you already had a link to the docs. Sorry for making you repeat yourself. I do appreciate the patients and understanding that you have shown. Greatly appreciated!!! – Rethabile Jan 11 '16 at 22:21
  • although he didn't say it, the OP needs both counts to appear in the same structure, and this code doesn't really work for that. – Alnitak Jan 12 '16 at 15:09
  • @Alnitak, that may be true, but the list of examples does not have a part for *monitor* in categories or what ever it maps to. i was asking that, but did not get any answer regarding the categories. so, what is the proper way do deal with it? – Nina Scholz Jan 12 '16 at 15:17
1

First count number of subcategories per category with reduce:

var subcategoriesCounts = subcats.reduce(function(acc, cur) {
    acc[cur.CategoryID] = (acc[cur.CategoryID] || 0) + 1;
    return acc;
}, {});

Then use map to transform categories to desired objects:

var result = categories.map(function(cat) {
    return {
        name: cat.Name,
        count: subcategoriesCounts[cat.ID] || 0
    }
});
miensol
  • 39,733
  • 7
  • 116
  • 112
0

var arr = [{ ticketId: 1, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 2, Category: "Driver", CategoryID: 29, SubCategory: "Monitor", SubCategoryID: 31 }, { ticketId: 3, Category: "Hardware", CategoryID: 11, SubCategory: "Monitor", SubCategoryID: 32 }, { ticketId: 4, Category: "Hardware", CategoryID: 11, SubCategory: "phone", SubCategoryID: 13 } ];

var map = {}


for(var i = 0; i < arr.length; i++) {
  console.log(arr[i])
    if (!map[arr[i].Category]) {
      map[arr[i].Category] = {
        count: 1,
        parentId: arr[i].CategoryID,
        categoryId: arr[i].SubCategoryID,
        categoryName: arr[i].SubCategory
      };
    } else {
      map[arr[i].Category].count++;
    }
}

var results = [];
for (var category in map) {
  results.push(map[category]);
}

var el = document.getElementById("result").innerHTML = JSON.stringify(results, null, '  ')
<pre id="result"></pre>
hampusohlsson
  • 10,109
  • 5
  • 33
  • 50
0

//FORMAT

var list = [
    {
        "ticketID": 1, 
        "category": "driver", 
        "subcategories": ['toner','power','paper jam','office applications']
    },
    {
        "ticketID": 2, 
        "category": "driver", 
        "subcategories": ['toner','power']
    },
];

Then you can simply get the length of the subs by console.log(list[0].subcategories.length)

as well as access them console.log(list[0].subcategories[0])

Jordan Davis
  • 1,485
  • 7
  • 21
  • 40
  • I definitely see what you are saying now. Thanks for taking the time to show the example. I will have to run this up the chain of command and see if we can get that API revised which shouldn't be a problem once I explain to them your example. Thank you for taking the time to reply!!! – Rethabile Jan 11 '16 at 22:15
  • @Rethabile yea let me know how it goes, just tag me on the thread when you find out so it notifies me. – Jordan Davis Jan 11 '16 at 22:22
  • I definitely will. I am actually writing the email as we speak. Better implementation is always appreciated and helps with efficiency. Thank you @jordan davis – Rethabile Jan 11 '16 at 22:24
0

To combine the counts for both levels in the same structure you have to separate the counts from the children. It'll cause no end of confusion if you both store the top level count as a property, and also the children themselves.

A better structure is produced by this:

var counts = data.reduce(function(r, o) {
    var cat = o.Category;
    var sub = o.SubCategory;

    r[cat] = r[cat] || { count: 0, children: {} }
    r[cat].count++;

    r[cat].children[sub] = r[cat].children[sub] || { count: 0 }
    r[cat].children[sub].count++;
    return r;
}, {});

which for your data will produce:

{
  "Driver": {
    count: 2,
    children: {
        "Monitor": { count: 2 }
    },
  },
  "Hardware": {
    count: 2,
    children: {
      "Monitor": { count: 1},
      "phone": { count: 1 },
    }
  }
}

and which supports additional levels of nesting if necessary.

Alnitak
  • 334,560
  • 70
  • 407
  • 495