397

Does anyone know of a way (lodash if possible too) to group an array of objects by an object key then create a new array of objects based on the grouping? For example, I have an array of car objects:

const cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

I want to make a new array of car objects that's grouped by make:

const cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}
double-beep
  • 5,031
  • 17
  • 33
  • 41
Trung Tran
  • 13,141
  • 42
  • 113
  • 200

33 Answers33

540

In plain Javascript, you could use Array#reduce with an object

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    result = cars.reduce(function (r, a) {
        r[a.make] = r[a.make] || [];
        r[a.make].push(a);
        return r;
    }, Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 3
    how can i iterate `result` results? – Mounir Elfassi Jul 18 '19 at 17:30
  • 3
    you could take the entries with [`Object.entries`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) and loop through the key/value pairs. – Nina Scholz Jul 18 '19 at 17:31
  • Is there a way to remove the `make` from data set once grouped? It's taking extra space. – Mercurial Oct 19 '19 at 19:33
  • yes, by [Rest in Object Destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Rest_in_Object_Destructuring). – Nina Scholz Oct 19 '19 at 19:36
  • What does r and a stand for? Would it be correct to assume that r is the accumulator and a the currentValue? – Omar Nov 07 '19 at 23:13
  • 4
    The best answer as ever. Why are you passing Object.create(null)? – kartik Jun 14 '20 at 11:38
  • @kartik, it creates an empty object without prototypes. – Nina Scholz Jun 14 '20 at 12:10
  • If you remove prototypes, How will the object has access to object's prototypes method? – kartik Jun 15 '20 at 09:04
  • @kartik, there is no one used here. – Nina Scholz Jun 15 '20 at 09:18
  • What does this line of code do r[a.make] = r[a.make] || []; – Sarah Jul 28 '20 at 01:23
  • 1
    @Sarah, it generates a new property `a.make` with an array if it does not exist (the value of not existing properties is `undefined`). if exist, it assign itself. – Nina Scholz Sep 28 '20 at 17:19
  • Why do you need this line Object.create(null)? I read your answer to kartik but I don t understand it. – Aalexander Dec 09 '20 at 10:46
  • @Alex, the reason is to prevent to have for example prototype functions like `toString` as property. for the most application you need not to worry about but you nee to know it, this is to show to use a really emypty object. – Nina Scholz Dec 09 '20 at 10:49
  • Have you a specific example when ` toString ` can be a property? – Aalexander Dec 09 '20 at 10:52
  • 1
    @Alex, please have a look here: https://stackoverflow.com/questions/38068424/push-a-array-object-into-another-array-object-if-the-value-not-exists/38068553#38068553 – Nina Scholz Dec 09 '20 at 10:56
  • Its shows following error [Vue warn]: Error in render: "TypeError: Cannot convert object to primitive value" – Sakthi Karthik Mar 23 '22 at 02:59
  • @SakthiKarthik, sorry, i can not help. i have no idea of *Vue*. – Nina Scholz Mar 23 '22 at 07:55
  • @NinaScholz could you help out here by any chance? https://stackoverflow.com/questions/72301893/get-value-of-object-key-and-parent-javascript-and-create-a-new-object/72302136#72302136 – peter flanagan May 19 '22 at 13:56
  • may I know how to understand this r[a.make] = r[a.make] || []; ? seems like assiging boolean value – Ae Leung Aug 03 '22 at 18:48
  • @NinaScholz May I know how Object Destructuring could remove the make field? – Ae Leung Aug 03 '22 at 18:53
  • @AeLeung, ad 1: its a [logical OR `||`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR), not a [bitwise OR `|`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR). ad 2: yes with a [Rest in Object Destructuring `...`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Rest_in_Object_Destructuring) and mapping this object. – Nina Scholz Aug 27 '22 at 18:02
  • Is there a way to make this use the order of the items in the array? I have sorted my array of objects by year descending e.g 2023, 2022, 2021, but this code example returns them in ascending order. – Mr Pablo Jan 09 '23 at 16:42
  • @MrPablo, objects have a given order, for positive 32 bit integer its sorted and coming first, then by insertation order. – Nina Scholz Jan 09 '23 at 17:01
199

Timo's answer is how I would do it. Simple _.groupBy, and allow some duplications in the objects in the grouped structure.

However the OP also asked for the duplicate make keys to be removed. If you wanted to go all the way:

var grouped = _.mapValues(_.groupBy(cars, 'make'),
                          clist => clist.map(car => _.omit(car, 'make')));

console.log(grouped);

Yields:

{ audi:
   [ { model: 'r8', year: '2012' },
     { model: 'rs5', year: '2013' } ],
  ford:
   [ { model: 'mustang', year: '2012' },
     { model: 'fusion', year: '2015' } ],
  kia: 
   [ { model: 'optima', year: '2012' } ] 
}

If you wanted to do this using Underscore.js, note that its version of _.mapValues is called _.mapObject.

Antoine V
  • 6,998
  • 2
  • 11
  • 34
Jonathan Eunice
  • 21,653
  • 6
  • 75
  • 77
152

You are looking for _.groupBy().

Removing the property you are grouping by from the objects should be trivial if required:

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}];

const grouped = _.groupBy(cars, car => car.make);

console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>
double-beep
  • 5,031
  • 17
  • 33
  • 41
TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94
138

There is absolutely no reason to download a 3rd party library to achieve this simple problem, like the above solutions suggest.

The one line version to group a list of objects by a certain key in es6:

const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})

The longer version that filters out the objects without the key:

function groupByKey(array, key) {
   return array
     .reduce((hash, obj) => {
       if(obj[key] === undefined) return hash; 
       return Object.assign(hash, { [obj[key]]:( hash[obj[key]] || [] ).concat(obj)})
     }, {})
}


var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'}];

console.log(groupByKey(cars, 'make'))

NOTE: It appear the original question asks how to group cars by make, but omit the make in each group. So the short answer, without 3rd party libraries, would look like this:

const groupByKey = (list, key, {omitKey=false}) => list.reduce((hash, {[key]:value, ...rest}) => ({...hash, [value]:( hash[value] || [] ).concat(omitKey ? {...rest} : {[key]:value, ...rest})} ), {})

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'}];

console.log(groupByKey(cars, 'make', {omitKey:true}))
magicgregz
  • 7,471
  • 3
  • 35
  • 27
  • this is definitely not es5 – Shinigami Mar 26 '18 at 06:34
  • 1
    Its just works !. Can any one elaborate this reduce function? – Jeevan Dec 28 '18 at 07:09
  • I liked both of your answers, but I see they both provide "make" field as a member of each "make" array. I've provided an answer based on yours where the delivered output matches the expected output. Thanks! – Daniel Vukasovich Dec 31 '18 at 19:09
  • @Jeevan `reduce` has as its argument a callback and an initial value. the callback has two args, previous and current value. Here, previous value is called `hash`. Maybe someone can explain more about its use here. It seems that `reduce` here reduces the array by a property which is extracted. – Timo Jul 24 '22 at 20:23
  • To anyone considering using this, be careful. The code is not optimised for large datasets, if you have a large array you want to group by, you're going to run into problems. I had an array of 33000 items that took almost 30 seconds to group with this. I'm pretty sure the problem is mainly in the use of `{...hash}` in the reducer, which creates lots of unused objects that waste space, leading to excessive memory use and garbage collection – Glen Keane Apr 21 '23 at 18:01
31

Here is your very own groupBy function which is a generalization of the code from: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore

function groupBy(xs, f) {
  return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {});
}

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const result = groupBy(cars, (c) => c.make);
console.log(result);
cdiggins
  • 17,602
  • 7
  • 105
  • 102
  • 1
    I love this answer due to the fact that you can use nested properties with it as well - very nice. I just changed it for Typescript and it was exactly what I was looking for :) – Alfa Bravo Oct 20 '21 at 13:41
  • 1
    shorter version: `groupBy=(x,f,r={})=>(x.forEach(v=>(r[f(v)]??=[]).push(v)),r)` – Endless Sep 05 '22 at 18:09
24

var cars = [{
  make: 'audi',
  model: 'r8',
  year: '2012'
}, {
  make: 'audi',
  model: 'rs5',
  year: '2013'
}, {
  make: 'ford',
  model: 'mustang',
  year: '2012'
}, {
  make: 'ford',
  model: 'fusion',
  year: '2015'
}, {
  make: 'kia',
  model: 'optima',
  year: '2012'
}].reduce((r, car) => {

  const {
    model,
    year,
    make
  } = car;

  r[make] = [...r[make] || [], {
    model,
    year
  }];

  return r;
}, {});

console.log(cars);
Aziz.G
  • 3,599
  • 2
  • 17
  • 35
  • need urgent help how to store above like [ { {"audi": [ { "model": "r8", "year": "2012" }] },{ {"ford": [ { "model": "r9", "year": "2021" }] } ...] each in object – Ravi Sharma Oct 28 '21 at 10:32
22

Its also possible with a simple for loop:

 const result = {};

 for(const {make, model, year} of cars) {
   if(!result[make]) result[make] = [];
   result[make].push({ model, year });
 }
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • And probably faster as well, and simpler. I've expanded your snippet to be a bit more dynamic as I had a long list of fields from a db table I didn't want to type in. Also note you will need to replace const with let. `for ( let { TABLE_NAME, ...fields } of source) { result[TABLE_NAME] = result[TABLE_NAME] || []; result[TABLE_NAME].push({ ...fields }); }` – adrien Jul 04 '19 at 07:12
  • TIL, thanks! https://medium.com/@mautayro/es6-variable-declaration-for-loops-why-const-works-in-a-for-in-loop-but-not-in-a-normal-a200cc5467c2 – adrien Jul 04 '19 at 18:57
12

I'd leave REAL GROUP BY for JS Arrays example exactly the same this task here

const inputArray = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

var outObject = inputArray.reduce(function(a, e) {
  // GROUP BY estimated key (estKey), well, may be a just plain key
  // a -- Accumulator result object
  // e -- sequentally checked Element, the Element that is tested just at this itaration

  // new grouping name may be calculated, but must be based on real value of real field
  let estKey = (e['Phase']); 

  (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
  return a;
}, {});

console.log(outObject);
ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
SynCap
  • 6,244
  • 2
  • 18
  • 27
11

You can try to modify the object inside the function called per iteration by _.groupBy func. Notice that the source array change his elements!

var res = _.groupBy(cars,(car)=>{
    const makeValue=car.make;
    delete car.make;
    return makeValue;
})
console.log(res);
console.log(cars);
nickxbs
  • 119
  • 4
  • 1
    While this code may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) of how and why this solves the problem would really help to improve the quality of your post. Remember that you are answering the question for readers in the future, not just the person asking now! Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply. – Makyen Nov 24 '16 at 05:33
  • It looks like the best answer to me since you go through the array only once to get the desired result. There's no need to use another function to remove the `make` property, and it is more readable as well. – Carrm Jun 29 '18 at 06:20
9

Just simple forEach loop will work here without any library

var cars = [
{
    'make': 'audi',
    'model': 'r8',
    'year': '2012'
}, {
    'make': 'audi',
    'model': 'rs5',
    'year': '2013'
}, {
    'make': 'ford',
    'model': 'mustang',
    'year': '2012'
}, {
    'make': 'ford',
    'model': 'fusion',
    'year': '2015'
}, {
    'make': 'kia',
    'model': 'optima',
    'year': '2012'
},
];
let ObjMap ={};

  cars.forEach(element => {
    var makeKey = element.make;
     if(!ObjMap[makeKey]) {
       ObjMap[makeKey] = [];
     }

    ObjMap[makeKey].push({
      model: element.model,
      year: element.year
    });
   });
   console.log(ObjMap);
Gautam
  • 1,668
  • 18
  • 17
7

Create a method which can be re-used

Array.prototype.groupBy = function(prop) {
      return this.reduce(function(groups, item) {
        const val = item[prop]
        groups[val] = groups[val] || []
        groups[val].push(item)
        return groups
      }, {})
    };

Then below you can group by any criteria

const groupByMake = cars.groupBy('make');
        console.log(groupByMake);

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];
  //re-usable method
Array.prototype.groupBy = function(prop) {
   return this.reduce(function(groups, item) {
  const val = item[prop]
  groups[val] = groups[val] || []
  groups[val].push(item)
  return groups
   }, {})
 };
  
 // initiate your groupBy. Notice the recordset Cars and the field Make....
  const groupByMake = cars.groupBy('make');
  console.log(groupByMake);
    
    //At this point we have objects. You can use Object.keys to return an array
Wahinya Brian
  • 663
  • 9
  • 18
7

Another one solution:

var cars = [
    {'make': 'audi','model': 'r8','year': '2012'}, {'make': 'audi','model': 'rs5','year': '2013'}, 
    {'make': 'ford','model': 'mustang','year': '2012'}, {'make': 'ford','model': 'fusion','year': '2015'}, 
    {'make': 'kia','model': 'optima','year': '2012'},
];


const reducedCars = cars.reduce((acc, { make, model, year }) => (
    { 
      ...acc, 
      [make]: acc[make] ? [ ...acc[make], { model, year }] : [ { model, year } ],
    }
 ), {});

console.log(reducedCars);
A1exandr Belan
  • 4,442
  • 3
  • 26
  • 48
5

Just try this one it works fine for me.

let grouped = _.groupBy(cars, 'make');

Note: Using lodash lib, so include it.

agravat.in
  • 561
  • 6
  • 14
  • 3
    Uncaught ReferenceError: _ is not defined - you should be clear that your solution require to install a 3rd party library just to solve this. – magicgregz Jan 09 '19 at 21:38
  • 4
    sorry, i think every one knows. _ stands and mostly used for lodash lib. so you need to use lodash. please read question so you will know he/she is asking for lodash. well thank you. i will remember this. and never forget to write lib. – agravat.in Jan 10 '19 at 09:57
  • 1
    You should edit your answer to include you're using a lib. – Rafael Oliveira Oct 05 '20 at 19:31
5

For cases where key can be null and we want to group them as others

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},
            {'make':'kia','model':'optima','year':'2033'},
            {'make':null,'model':'zen','year':'2012'},
            {'make':null,'model':'blue','year':'2017'},

           ];


 result = cars.reduce(function (r, a) {
        key = a.make || 'others';
        r[key] = r[key] || [];
        r[key].push(a);
        return r;
    }, Object.create(null));
exexzian
  • 7,782
  • 6
  • 41
  • 52
5

Agree that unless you use these often there is no need for an external library. Although similar solutions are available, I see that some of them are tricky to follow here is a gist that has a solution with comments if you're trying to understand what is happening.

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

/**
 * Groups an array of objects by a key an returns an object or array grouped by provided key.
 * @param array - array to group objects by key.
 * @param key - key to group array objects by.
 * @param removeKey  - remove the key and it's value from the resulting object.
 * @param outputType - type of structure the output should be contained in.
 */
const groupBy = (
  inputArray,
  key,
  removeKey = false,
  outputType = {},
) => {
  return inputArray.reduce(
    (previous, current) => {
      // Get the current value that matches the input key and remove the key value for it.
      const {
        [key]: keyValue
      } = current;
      // remove the key if option is set
      removeKey && keyValue && delete current[key];
      // If there is already an array for the user provided key use it else default to an empty array.
      const {
        [keyValue]: reducedValue = []
      } = previous;

      // Create a new object and return that merges the previous with the current object
      return Object.assign(previous, {
        [keyValue]: reducedValue.concat(current)
      });
    },
    // Replace the object here to an array to change output object to an array
    outputType,
  );
};

console.log(groupBy(cars, 'make', true))
wattry
  • 936
  • 10
  • 22
4
function groupBy(data, property) {
  return data.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}
groupBy(people, 'age');
sama vamsi
  • 197
  • 1
  • 4
3

You can also make use of array#forEach() method like this:

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

let newcars = {}

cars.forEach(car => {
  newcars[car.make] ? // check if that array exists or not in newcars object
    newcars[car.make].push({model: car.model, year: car.year})  // just push
   : (newcars[car.make] = [], newcars[car.make].push({model: car.model, year: car.year})) // create a new array and push
})

console.log(newcars);
BlackBeard
  • 10,246
  • 7
  • 52
  • 62
3

Prototype version using ES6 as well. Basically this uses the reduce function to pass in an accumulator and current item, which then uses this to build your "grouped" arrays based on the passed in key. the inner part of the reduce may look complicated but essentially it is testing to see if the key of the passed in object exists and if it doesn't then create an empty array and append the current item to that newly created array otherwise using the spread operator pass in all the objects of the current key array and append current item. Hope this helps someone!.

Array.prototype.groupBy = function(k) {
  return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{});
};

const projs = [
  {
    project: "A",
    timeTake: 2,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 4,
    desc: "this is a description"
  },
  {
    project: "A",
    timeTake: 12,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 45,
    desc: "this is a description"
  }
];

console.log(projs.groupBy("project"));
Azayda
  • 81
  • 2
  • 3
3

A proposal that adds Object.groupBy() and Map.groupBy() is now on Stage 3!

When it reaches Stage 4 and is implemented on most major browsers, you'll be able to do this:

const cars = [
  { make: 'audi', model: 'r8', year: '2012' },
  { make: 'audi', model: 'rs5', year: '2013' },
  { make: 'ford', model: 'mustang', year: '2012' },
  { make: 'ford', model: 'fusion', year: '2015' },
  { make: 'kia', model: 'optima', year: '2012' }
];

const grouped = Object.groupBy(cars, item => item.make);
console.log(grouped);

which will output:

{
  audi: [
    { make: 'audi', model: 'r8', year: '2012' },
    { make: 'audi', model: 'rs5', year: '2013' }
  ],
  ford: [
    { make: 'ford', model: 'mustang', year: '2012' },
    { make: 'ford', model: 'fusion', year: '2015' }
  ],
  kia: [
    { make: 'kia', model: 'optima', year: '2012' }
  ]
}

Until then, you can use this core-js polyfill:

const cars = [
  { make: 'audi', model: 'r8', year: '2012' },
  { make: 'audi', model: 'rs5', year: '2013' },
  { make: 'ford', model: 'mustang', year: '2012' },
  { make: 'ford', model: 'fusion', year: '2015' },
  { make: 'kia', model: 'optima', year: '2012' }
];

const grouped = Object.groupBy(cars, item => item.make);
//console.log(grouped);

// Optional: remove the "make" property from resulting object
const entriesUpdated = Object
  .entries(grouped)
  .map(([key, value]) => [
    key,
    value.map(({make, ...rest}) => rest)
  ]);
const noMake = Object.fromEntries(entriesUpdated);
console.log(noMake);
<script src="https://unpkg.com/core-js-bundle@3.31.1/minified.js"></script>
double-beep
  • 5,031
  • 17
  • 33
  • 41
2

I liked @metakunfu answer, but it doesn't provide the expected output exactly. Here's an updated that get rid of "make" in the final JSON payload.

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})

console.log(JSON.stringify(result));

Output:

{  
   "audi":[  
      {  
         "model":"r8",
         "year":"2012"
      },
      {  
         "model":"rs5",
         "year":"2013"
      }
   ],
   "ford":[  
      {  
         "model":"mustang",
         "year":"2012"
      },
      {  
         "model":"fusion",
         "year":"2015"
      }
   ],
   "kia":[  
      {  
         "model":"optima",
         "year":"2012"
      }
   ]
}
Daniel Vukasovich
  • 1,692
  • 1
  • 18
  • 26
1
const reGroup = (list, key) => {
    const newGroup = {};
    list.forEach(item => {
        const newItem = Object.assign({}, item);
        delete newItem[key];
        newGroup[item[key]] = newGroup[item[key]] || [];
        newGroup[item[key]].push(newItem);
    });
    return newGroup;
};
const animals = [
  {
    type: 'dog',
    breed: 'puddle'
  },
  {
    type: 'dog',
    breed: 'labradoodle'
  },
  {
    type: 'cat',
    breed: 'siamese'
  },
  {
    type: 'dog',
    breed: 'french bulldog'
  },
  {
    type: 'cat',
    breed: 'mud'
  }
];
console.log(reGroup(animals, 'type'));
const cars = [
  {
      'make': 'audi',
      'model': 'r8',
      'year': '2012'
  }, {
      'make': 'audi',
      'model': 'rs5',
      'year': '2013'
  }, {
      'make': 'ford',
      'model': 'mustang',
      'year': '2012'
  }, {
      'make': 'ford',
      'model': 'fusion',
      'year': '2015'
  }, {
      'make': 'kia',
      'model': 'optima',
      'year': '2012'
  },
];

console.log(reGroup(cars, 'make'));
AKelley
  • 21
  • 2
1

Grouped Array of Object in typescript with this:

groupBy (list: any[], key: string): Map<string, Array<any>> {
    let map = new Map();
    list.map(val=> {
        if(!map.has(val[key])){
            map.set(val[key],list.filter(data => data[key] == val[key]));
        }
    });
    return map;
});
Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
1

I love to write it with no dependency/complexity just pure simple js.

const mp = {}
const cars = [
  {
    model: 'Imaginary space craft SpaceX model',
    year: '2025'
  },
  {
    make: 'audi',
    model: 'r8',
    year: '2012'
  },
  {
    make: 'audi',
    model: 'rs5',
    year: '2013'
  },
  {
    make: 'ford',
    model: 'mustang',
    year: '2012'
  },
  {
    make: 'ford',
    model: 'fusion',
    year: '2015'
  },
  {
    make: 'kia',
    model: 'optima',
    year: '2012'
  }
]

cars.forEach(c => {
  if (!c.make) return // exit (maybe add them to a "no_make" category)

  if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }]
  else mp[c.make].push({ model: c.model, year: c.year })
})

console.log(mp)
1

I made a benchmark to test the performance of each solution that don't use external libraries.

JSBen.ch

The reduce() option, posted by @Nina Scholz seems to be the optimal one.

leonardofmed
  • 842
  • 3
  • 13
  • 47
1
letfinaldata=[]

let data =[{id:1,name:"meet"},{id:2,name:"raj"},{id:1,name:"hari"},{id:3,name:"hari"},{id:2,name:"ram"}]

data = data.map((item)=> 
{
    return {...item,
        name: [item.name]
    }
}) // Converting the name key from string to array


let temp = [];

for(let i =0 ;i<data.length;i++)
{
    const index = temp.indexOf(data[i].id) // Checking if the object id is already present
    if(index>=0)
    {
        letfinaldata[index].name = [...letfinaldata[index].name,...data[i].name] // If present then append the name to the name of that object
    }
    else{
        temp.push(data[i].id); // Push the checked object id
        letfinaldata.push({...data[i]}) // Push the object
    }
}

console.log(letfinaldata)

Output

[ { id: 1, name: [ 'meet', 'hari' ] },
  { id: 2, name: [ 'raj', 'ram' ] },
  { id: 3, name: [ 'hari' ] } ]
Lahfir
  • 145
  • 2
  • 15
0

With lodash/fp you can create a function with _.flow() that 1st groups by a key, and then map each group, and omits a key from each item:

const { flow, groupBy, mapValues, map, omit } = _;

const groupAndOmitBy = key => flow(
  groupBy(key),
  mapValues(map(omit(key)))
);

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const groupAndOmitMake = groupAndOmitBy('make');

const result = groupAndOmitMake(cars);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

Building on the answer by @Jonas_Wilms if you do not want to type in all your fields:

    var result = {};

    for ( let { first_field, ...fields } of your_data ) 
    { 
       result[first_field] = result[first_field] || [];
       result[first_field].push({ ...fields }); 
    }

I didn't make any benchmark but I believe using a for loop would be more efficient than anything suggested in this answer as well.

adrien
  • 504
  • 1
  • 7
  • 17
0

Here is a solution inspired from Collectors.groupingBy() in Java:

function groupingBy(list, keyMapper) {
  return list.reduce((accummalatorMap, currentValue) => {
    const key = keyMapper(currentValue);
    if(!accummalatorMap.has(key)) {
      accummalatorMap.set(key, [currentValue]);
    } else {
      accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue));
    }
    return accummalatorMap;
  }, new Map());
}

This will give a Map object.

// Usage

const carMakers = groupingBy(cars, car => car.make);
Rahul Sethi
  • 349
  • 2
  • 5
0
const groupBy = (array, callback) => {
  const groups = {};
  
  array.forEach((element) => {
    const groupName = callback(element);
    if (groupName in groups) {
      groups[groupName].push(element);
    } else {
      groups[groupName] = [element];
    }
  });
  
  return groups;
};

or for fancy pants:

(() => {
  Array.prototype.groupBy = function (callback) {
    const groups = {};
    this.forEach((element, ...args) => {
      const groupName = callback(element, ...args);
      if (groupName in groups) {
        groups[groupName].push(element);
      } else {
        groups[groupName] = [element];
      }
    });

    return groups;
  };
})();

const res = [{ name: 1 }, { name: 1 }, { name: 0 }].groupBy(({ name }) => name);

// const res = {
//   0: [{name: 0}],
//   1: [{name: 1}, {name: 1}]
// }

This is a polyfill for the MDN Array.groupBy function.

Stan
  • 1,800
  • 1
  • 13
  • 15
0

Try

groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )

const cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

const groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )

console.log('gr', groupBy(cars, o=>o.make));

This answer is inspired by cdiggins answer and Endless comment (without key remove in final objects). The improvement is that we have same small size but function interface groupBy(a,f) not contains additional redundant variables. To get array of grouped arrays you can use Object.values(groupBy(cars, o=>o.make))

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
-1

Here is another solution to it. As requested.

I want to make a new array of car objects that's grouped by make:

function groupBy() {
  const key = 'make';
  return cars.reduce((acc, x) => ({
    ...acc,
    [x[key]]: (!acc[x[key]]) ? [{
      model: x.model,
      year: x.year
    }] : [...acc[x[key]], {
      model: x.model,
      year: x.year
    }]
  }), {})
}

Output:

console.log('Grouped by make key:',groupBy())
EugenSunic
  • 13,162
  • 13
  • 64
  • 86
-1

Slightly different version of @metakungfus answer, main difference being that it omits the original key from the resulting objects since it's no longer needed on the object itself in some cases since it's now available in the parent object.

const groupBy = (_k, a) => a.reduce((r, {[_k]:k, ...p}) => ({
    ...r, ...{[k]: (
        r[k] ? [...r[k], {...p}] : [{...p}]
    )}
}), {});

Considering your original input object:

console.log(groupBy('make', cars));

Would result in:

{
  audi: [
    { model: 'r8', year: '2012' },
    { model: 'rs5', year: '2013' }
  ],
  ford: [
    { model: 'mustang', year: '2012' },
    { model: 'fusion', year: '2015' }
  ],
  kia: [
    { model: 'optima', year: '2012' }
  ]
}
Nathan F.
  • 3,250
  • 3
  • 35
  • 69
-1

This is a generic function which will return Array groupBy its own key.

const getSectionListGroupedByKey = < T > (
  property: keyof T,
  List: Array < T >
): Array < {
  title: T[keyof T];data: Array < T >
} > => {
  const sectionList: Array < {
    title: T[keyof T];data: Array < T >
  } > = [];

  if (!property || !List ? .[0] ? .[property]) {
    return [];
  }

  const groupedTxnListMap: Map < T[keyof T], Array < T >> = List.reduce((acc, cv) => {
    const keyValue: T[keyof T] = cv[property];

    if (acc.has(keyValue)) {
      acc.get(keyValue) ? .push(cv);
    } else {
      acc.set(keyValue, [cv]);
    }

    return acc;
  }, new Map < T[keyof T], Array < T >> ());

  groupedTxnListMap.forEach((value, key) => {
    sectionList.push({
      title: key,
      data: value
    });
  });

  return sectionList;
};


// Example
const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

const result = getSectionListGroupedByKey('make', cars);
console.log('result: ', result)
Abhinav Chandra
  • 227
  • 2
  • 3