JavaScript is finally getting the array grouping method which means we don’t have to rely on lodash to be able to group our objects by some key. It’s already at stage 3 of the ECMAScript proposal process which means it’s going to land in Node.js and other browsers very soon. Although it is already in Chrome 117. In this tutorial, let’s take a look at how it works.
How We Typically Group Items in JavaScript
Let’s say we have an array of fruits and we want to group the objects based on the calories. We might use a forEach loop like this:
const fruits = [
{ name: "Banana", calories: 28 },
{ name: "Apple", calories: 30 },
{ name: "Avacado", calories: 28 },
];
const fruitByCalories = {};
fruits.forEach((fruit) => {
const calories = fruit.calories;
if (!fruitByCalories[calories]) {
fruitByCalories[calories] = [];
}
fruitByCalories[calories].push(fruit);
});
console.log(JSON.stringify(fruitByCalories, null, 2));
or we can reduce it like this:
const fruits = [
{ name: "Banana", calories: 28 },
{ name: "Apple", calories: 30 },
{ name: "Avocado", calories: 28 },
];
const fruitsByCalories = fruits.reduce((acc, fruit) => {
const calories = fruit.calories;
if (!acc[calories]) {
acc[calories] = [];
}
acc[calories].push(fruit);
return acc;
}, {});
console.log(JSON.stringify(fruitsByCalories, null, 2));
Output:
For both the above codes the output will be:
In any case, the code is rather awkward. You must always verify the object to see if the grouping key exists, and if it does not, create it using an empty array. Then you can push the item into the array.
Grouping Items in JavaScript with groupBy()
Now luckily we are getting these new methods that work with objects and maps. These methods can be utilized for grouping objects or mapping data.
Object.groupBy()
Object.groupBy accepts items that should be iterable, such as an array, and a callback function, which is similar to other methods. Callback receives an element that the iterable is currently on, and optionally an index if you want to use it. You can use it like this:
Syntax
Object.groupBy(items, callbackFn)
- items: An array of items that you want to group. Each item in the array could be an object, a primitive value, or any other data type.
- callbackFn: The callback function that is used to determine the grouping key for each item. The callback function takes each item from the array as input and returns the grouping key for that item. The grouping key can be any value, such as a string, number, or even an object.
Example
From the above example of grouping fruits, we can do the same operation using Object.groupBy().
const fruitsByCalories = Object.groupBy(fruits, (fruit) => fruit.calories);
console.log(fruitsByCalories);
However, it’s important to note that the return value is going to be a null-prototype Object. This means that the object does not inherit any properties from “Object.prototype”. It also prevents a lot of security risks which means an intruder is not able to pollute the prototype of an object.
Output:
Map.groupBy()
Map.groupBy is essentially identical to Object.groupBy, except that it returns a Map. This implies you can use all of the standard Map functions. This also implies that you can return any type of value from the callback function.
Syntax
Map.groupBy(items, callbackFn)
- items: An array of items that you want to group. Each item in the array could be an object, a primitive value, or any other data type.
- callbackFn: The callback function that is used to determine the grouping key for each item. The callback function takes each item from the array as input and returns the grouping key for that item. The grouping key can be any value, such as a string, number, or even an object.
Example
In the below case, we are grouping fruits by ‘countsWith’ property. Note that the objects have to have the same identity to retrieve items from this Map.
const intake = { name: "Vegetable", calories: 100, countsWith: null };
const food = { name: "Apple", calories: 30, countsWith: intake };
const fruits = [
intake,
food,
{ name: "Banana", calories: 28 },
{ name: "Avocado", calories: 28 },
];
const fruitsByFood = Map.groupBy(fruits, (fruit) => fruit.countsWith);
console.log(fruitsByFood.get(intake));
console.log(fruitsByFood.get({ name: "Vegetable", calories: 100, countsWith: null }));
Output:
We should use the same reference of the object that was used as the key when populating the map. Since the “get” method of a Map checks for strict equality when it is comparing keys, passing different references to an object with the same properties won’t match the reference stored in the map which results in an “undefined”.
Browser Compatibility
The two groupBy approaches are part of a TC39 proposal that is now in stage 3. This indicates it has a strong possibility of becoming a standard, and as a result, implementations are surfacing.
Chrome 117 just added support for these two approaches, and Firefox 119 followed suit. Safari has previously implemented similar techniques under various names, I’m sure they’ll change them shortly. The methods are in Chrome, which suggests they have been implemented in V8 and will be available in Node the next time V8 is updated.
Conclusion
The introduction of JavaScript’s groupBy functions for objects and maps simplifies array grouping jobs. These techniques are simpler and more elegant than old ways, allowing developers to quickly group items based on given criteria. Developers may use Object.groupBy and Map.groupBy to generate clearer code and increase security, making them important tools for data manipulation in JavaScript.
Also Read:
References
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/groupBy