14

I have a heavy array like this:

[
  {Id: 1, Name: 'Red', optionName: 'Color'}, 
  {Id: 2, Name: 'Yellow', optionName: 'Color'},
  {Id: 3, Name: 'Blue', optionName: 'Color'},
  {Id: 4, Name: 'Green', optionName: 'Color'},
  {Id: 7, Name: 'Black', optionName: 'Color'},
  {Id: 8, Name: 'S', optionName: 'Size'},
  {Id: 11, Name: 'M', optionName: 'Size'},
  {Id: 12, Name: 'L', optionName: 'Size'},
  {Id: 13, Name: 'XL', optionName: 'Size'},
  {Id: 14, Name: 'XXL', optionName: 'Size'}
]

What I need to do is to group them by optionName and have two row in the main array like this:

[
  {
    Name: 'Color',
    Data:[{Id: 1, Name: 'Red'},
          {Id: 2, Name: 'Yellow'},
          {Id: 3, Name: 'Blue'},
          {Id: 4, Name: 'Green'},
          {Id: 7, Name: 'Black'}]
  }, {
    Name: 'Size',
    Data:[{Id: 8, Name: 'S'},
          {Id: 11, Name: 'M'},
          {Id: 12, Name: 'L'},
          {Id: 13, Name: 'XL'},
          {Id: 14, Name: 'XXL'}]
  }
]

How to do it in javascript?

0

8 Answers 8

16

Nov 17th, 2023 Update:

You can also use Object.groupBy:

Doing something like Object.groupBy(list, item => item.optionName) gives you:

{
    "Color": [
        {
            "Id": 1,
            "Name": "Red",
            "optionName": "Color"
        },
        {
            "Id": 2,
            "Name": "Yellow",
            "optionName": "Color"
        },
        ...
    ],
    "Size": [
        {
            "Id": 8,
            "Name": "S",
            "optionName": "Size"
        },
        {
            "Id": 11,
            "Name": "M",
            "optionName": "Size"
        },
        ...
    ]
}

Original Answer:

This is a snippet I wrote for these kind of situations. You can add this functionality to all of your arrays:

Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    var map = {};
    this.forEach(function (e) {
      var k = key(e);
      map[k] = map[k] || [];
      map[k].push(e);
    });
    return Object.keys(map).map(function (k) {
      return {key: k, data: map[k]};
    });
  }
});

You can use it like this. You can just pass a function which defines how you want to group your data.

var newArray = arr.group(function (item) {
  return item.optionName;
});

Working Fiddle

If you need, you can replace {key: k, data: map[k]} with {Name: k, Data: map[k]}.


This is also more compact ES6 version of the code above:

Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    let map = {};
    this.map(e => ({k: key(e), d: e})).forEach(e => {
      map[e.k] = map[e.k] || [];
      map[e.k].push(e.d);
    });
    return Object.keys(map).map(k => ({key: k, data: map[k]}));
  }
});

Use it like this:

let newArray = arr.group(item => item.optionName))
Sign up to request clarification or add additional context in comments.

Comments

3

An ES6 solution using Map object:

function groupBy(arr, key) {
  	return arr.reduce(
      (sum, item) => {
      	const groupByVal = item[key];
        groupedItems = sum.get(groupByVal) || [];
        groupedItems.push(item);
      	return sum.set(groupByVal, groupedItems);
        },
      new Map()
      );
}

var Data = [ 
{ Id: 1, Name: 'Red', optionName: 'Color' }, 
  { Id: 2, Name: 'Yellow', optionName: 'Color' },
  { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];

document.getElementById("showArray").innerHTML =JSON.stringify([...groupBy(Data, 'optionName')], null, 4); 
<pre id="showArray"></pre>

Comments

3

You can use reduce to get the resultset you need:

var result = list.reduce(function(memo, item) {
  if (item.optionName === 'Color') {
    memo[0].Data.push(
      Id: item.Id,
      Name: item.Name
    });
  }
  if (item.optionName === 'Size') {
    memo[1].Data.push({
      Id: item.Id,
      Name: item.Name
    });
  }
  return memo;
}, [{ Name: 'Color', Data: [] }, { Name: 'Size', Data: [] }]);

variable list is your first list.

Hope this helps.

Comments

1

This is a snippet I wrote for kind of my situation in my application functionality of all arrays. This snippet code is use in node js application. All the above is is given solution but I was finding some problem in server side in node js. This snippet is user full me....

  var Data= [ 
    { Id: 1, Name: 'Red', optionName: 'Color' }, 
      { Id: 2, Name: 'Yellow', optionName: 'Color' },
      { Id: 3, Name: 'Blue', optionName: 'Color' },
      { Id: 4, Name: 'Green', optionName: 'Color' },
      { Id: 7, Name: 'Black', optionName: 'Color' },
      { Id: 8, Name: 'S', optionName: 'Size' },
      { Id: 11, Name: 'M', optionName: 'Size' },
      { Id: 12, Name: 'L', optionName: 'Size' },
      { Id: 13, Name: 'XL', optionName: 'Size' },
      { Id: 14, Name: 'XXL', optionName: 'Size' } ];

    function groupBy(arr, key) {
                var newArr = [],
                    types = {},
                    newItem, i, j, cur;
                for (i = 0, j = arr.length; i < j; i++) {
                    cur = arr[i];
                    if (!(cur[key] in types)) {
                        types[cur[key]] = { type: cur[key], data: [] };
                        newArr.push(types[cur[key]]);
                    }
                    types[cur[key]].data.push(cur);
                }
                return newArr;
}

I use it like this. I just pass a function which defines how you want to group our data.

filterData= groupBy(Data,'optionName');

Result of this snippet of code output.....

 [
{"type":"Color","data":[{"Id":1,"Name":"Red","optionName":"Color"},
                       {"Id":2,"Name":"Yellow","optionName":"Color"},
                       {"Id":3,"Name":"Blue","optionName":"Color"}, 
                       {"Id":4,"Name":"Green","optionName":"Color"},
                       {"Id":7,"Name":"Black","optionName":"Color"}]},
{"type":"Size","data":[{"Id":8,"Name":"S","optionName":"Size"},
                       {"Id":11,"Name":"M","optionName":"Size"},
                       {"Id":12,"Name":"L","optionName":"Size"},
                       {"Id":13,"Name":"XL","optionName":"Size"},
                       {"Id":14,"Name":"XXL","optionName":"Size"}]}
]

Show on fiddle

Comments

0
var originalList = [ { Id: 1, Name: 'Red', optionName: 'Color' }, 
  { Id: 2, Name: 'Yellow', optionName: 'Color' },
  { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];

var output = [{ Name: "Color", Data: [] },{ Name: "Size", Data: [] }] ;

originalList.map(function(entry){
    if ( entry.optionName === "Color") output[0].Data.push({ Id: entry.Id, Name: entry.Name });
    if ( entry.optionName === "Size") output[1].Data.push({ Id: entry.Id, Name: entry.Name });
});

Comments

0
'use strict'

let l = [ { Id: 1, Name: 'Red', optionName: 'Color' }, 
          { Id: 2, Name: 'Yellow', optionName: 'Color' },
          { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];


let color = [];
let size  = [];

l.forEach(element => {
    if (element['optionName'] === 'Color') {
      color.push({'Id': element.Id, 'Name': element.Name});
    } else {
      size.push({'Id': element.Id, 'Name': element.Name});
    }
});  


console.log(color);
console.log(size);

You can try this method.

Comments

0

All of the answers lead to the same result, so it all comes down to a personal preference (or company guidelines) on how to tackle this.

//  ES5 (traditional javascript) version
function groupByOptionName(list, optionName) {
    return list
        //  filter out any item whose optionName does not match the desired name
        .filter(function(item) {
            return item.optionName === optionName;
        })
        //  map the item into the desired shape 
        //  (appears to be everything except optionName itself
        .map(function(item) {
            return {
                Id: item.Id,
                Name: item.Name
            };
        })
}

//  ES2015/ES6 version
function groupByOptionName(list, optionName) {
    return list
        //  filter out any item whose optionName does not match the desired name
        .filter(item => item.optionName === optionName)
        //  map the item into the desired shape 
        //  (appears to be everything except optionName itself
        .map(item => {
            Id: item.Id,
            Name: item.Name
        });
}

This function would let you program the desired result as follows:

var output = [
    {Name: 'Color', Data: groupByOptionName(list, 'Color')},
    {Name: 'Size', Data: groupByOptionName(list, 'Size')},
];
//  the ES2015/ES6 version of this code would replace var with let

While the code itself differs, it is much like the other answers, with only a variation on the steps needed.

One could also opt to leave out any hardcoded option names (Color and Size) by extracting those aswel, this would allow for a more dynamic input, but could also introduce more processing that actually needed.

//  ES5 (traditional javascript) version
function getOptionNames(list) {
    return list
        //  map the array into a list of optionNames
        .map(function(item) {
            return item.optionName;
        })
        //  remove duplicates
        .filter(function(item, index, all) {
            return all.indexOf(item) === index;
        });
}

//  ES2015/ES6 version (does exactly the same as the one above)
function getOptionNames(list) {
    return list
        //  map the array into a list of optionNames
        .map(item => item.optionName)
        //  remove duplicates
        .filter((item, index, all) => all.indexOf(item) === index);
}

Which allows the result to be fully based on the input data:

//  ES5 (traditional javascript) version
var output = getOptionNames(list)
        //  map the names into the desired structure
        .map(function(buffer, name) {
            return {
                Name: name,
                Data: groupByOptionName(list, name)
            };
        });

//  ES2015/ES6 version (does exactly the same as the one above)
var output = getOptionNames(list)
        //  map the names into the desired structure
        .map((buffer, name) => {
            Name: name,
            Data: groupByOptionName(list, name)
        });

By writing all of the data-mangling steps in short consice steps you'd do yourself (especially your future self) a favor if this code ever needs to be adjusted.

If the data set really is heavy (in terms of a lot of data), you must also make sure to keep the number of copies you keep in memory limited. For example, if you never need the original dataset, make sure it can be garbage collected (by not having a variable containing it outside the scope where you receive the data)

4 Comments

Good summary Rogier, just one correction tough: you mentioned the solutions available in ES6+, but array prototype filter, map and reduce is available since ES5.1 (2011) and supported by IE9+, FF3+, and all chrome versions.
@AhmetCetin I'm not entirely sure if I understand what you mean, in my example I provide the ES5(.1) syntax aswel as the same function written in ES6+. Maybe I need to clarify the first functions to be ES5(.1)?
you're right, i missed that, you used fat arrow notation in ES6.would be better for understanding i guess if you mention the other syntax is valid for ES5.1. When I read first time, I thought you meant all solutions valid for ES6.
@AhmetCetin thanks for your feedback, I think I made it more clear now
0

Usage:

groupValues([
    { color: 'blue', value: 100 }, 
    { color: 'blue', value: 75 }, 
    { color: 'yellow', value: 50 },
    { color: 'yellow', value: 25 }
], 'color')

Result:

[
    [{ color: 'blue', value: 100 }, { color: 'blue', value: 75 }],
    [{ color: 'yellow', value: 50 }, { color: 'yellow', value: 25 }] 
]

Function:

const groupValues = function(arr, key) {
    const mapped = {}
    arr.forEach(el => {
        const actualKey = el[key]
        if(!mapped.hasOwnProperty(actualKey)) mapped[actualKey] = []

        mapped[actualKey].push(el)
    })
    return Object.keys(mapped).map(el => mapped[el])
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.