Reputation: 2193
I have an array of vehicles that need to be grouped by make and model, only if the 'selected' property is true. The resulting object should contain properties for make model and count. Using lodash, how can I organize the vehicle objects into the desired result objects. I'm able to get the vehicle objects grouped by makeCode but I'm not sure how to group by more than one property.
group by make code works
var vehicles = _.groupBy(response.vehicleTypes, function(item)
{
return item.makeCode; // how to group by model code as well
});
initial vehicles
{
id: 1,
selected: true,
makeCode: "Make-A",
modelCode: "Model-a",
trimCode: "trim-a",
yearCode: "2012"
},
{
id: 2,
selected: false,
makeCode: "Make-A",
modelCode: "Model-a",
trimCode: "trim-a",
yearCode: "2013"
},
{
id: 3,
selected: true,
makeCode: "Make-B",
modelCode: "Model-c",
trimCode: "trim-a",
yearCode: "2014"
},
{
id: 25,
selected: true,
makeCode: "Make-C",
modelCode: "Model-b",
trimCode: "trim-b",
yearCode: "2012"
},
{
id: 26,
selected: true,
makeCode: "Make-C",
modelCode: "Model-b",
trimCode: "trim-a",
yearCode: "2013"
}
result object
{
Make-A: {
Model-a: {
count: 1
}
}
},
{
Make-B: {
Model-c: {
count: 1
}
}
},
{
Make-C: {
Model-b: {
count: 2
}
}
}
Upvotes: 38
Views: 93369
Reputation: 21
For those you are looking something similar as the owner but don't have a boolean as a filter as first property, you can do a double groupby:
export function groupByLodash(results: myType[]) {
return mapValues(
groupBy(results, 'property.to.group'),
(result: myType) => groupBy(result, 'second.property.to.group'),
);
}
the result sould be something like:
{
"property.to.group": { "second.property.to.group": {...result}}
}
Upvotes: 2
Reputation: 3359
Following code works if you focus on result:
in my case, Brand
and Item Code
are properties
const products = _.groupBy(this.productsTable.data, (item) => {
return [item['Brand'], item['Item Code']];
});
Upvotes: 23
Reputation: 9
You don't need to reiterate all the array object. with loadash, you can make simple as below:
_.mapValues(_.groupBy(object, 'yourKeys'))
Upvotes: 0
Reputation: 1434
I am not sure if this will solve Your problem, but in group_by You can add custom logic which allow You to create composite key.
Remember that SEPERATOR value should be defined accordingly to data source You work with, if "--" characters occur in model or type You should not used them as it will not allow You to reverse the process of grouping.
const SEPERATOR = "--";
_.chain(data).filter((item) => item.selected).groupBy((item)=>`${item.model}${SEPERATOR}${item.type}`).value();
Upvotes: 47
Reputation: 192252
You can use Array.prototype.reduce()
, and do it in one loop O(n):
var arr = [{"id":1,"selected":true,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2012"},{"id":2,"selected":false,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2013"},{"id":3,"selected":true,"makeCode":"Make-B","modelCode":"Model-c","trimCode":"trim-a","yearCode":"2014"},{"id":25,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-b","yearCode":"2012"},{"id":26,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-a","yearCode":"2013"},{"id":29,"selected":false,"makeCode":"Make-A","modelCode":"Model-g","trimCode":"trim-a","yearCode":"2013"},{"id":2,"selected":true,"makeCode":"Make-A","modelCode":"Model-h","trimCode":"trim-a","yearCode":"2013"}];
var result = arr.reduce(function(map, obj) {
if(!obj.selected) {
return map;
}
var makeCode = map[obj.makeCode] = map[obj.makeCode] || {};
var modelCode = makeCode[obj.modelCode] = makeCode[obj.modelCode] || { count: 0 };
modelCode.count++;
return map;
}, Object.create(null));
console.log(result);
A more readable, and modern version using destructuring:
const arr = [{"id":1,"selected":true,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2012"},{"id":2,"selected":false,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2013"},{"id":3,"selected":true,"makeCode":"Make-B","modelCode":"Model-c","trimCode":"trim-a","yearCode":"2014"},{"id":25,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-b","yearCode":"2012"},{"id":26,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-a","yearCode":"2013"},{"id":29,"selected":false,"makeCode":"Make-A","modelCode":"Model-g","trimCode":"trim-a","yearCode":"2013"},{"id":2,"selected":true,"makeCode":"Make-A","modelCode":"Model-h","trimCode":"trim-a","yearCode":"2013"}];
const result = arr.reduce((acc, { selected, makeCode, modelCode }) => {
if(!selected) return acc;
if(!acc[makeCode]) acc[makeCode] = {};
const make = acc[makeCode];
if(!make[modelCode]) make[modelCode] = { count: 0 };
make[modelCode].count++;
return acc;
}, Object.create(null));
console.log(result);
Upvotes: 8
Reputation: 35
Grouping and filtering an object or array without LODASH on pure JS:
// Группировка и фильтрация объекта или массива без LODASH на чистом JS:
let k = "HUID", // group by;
input = [
{HUID:11,test:1},
{HUID:11,test:111},
{HUID:'eeeeeeeeeeee',test:11111},
{HUID:22,test:2},
{HUID:33,test:3}
],
result = input.reduce((map, obj) => {
//if(!obj.selected) { return map; }
let makeCode = (map[obj[k]] = map[obj[k]] || {}); // var modelCode = makeCode[obj.HUID] = makeCode[obj.HUID] || { count: 0 };
let l = map[obj[k]],
m = Object.keys(l).length;
l[m] = { ...obj };
return map;
}, {} );
console.log(result);
Upvotes: 0
Reputation: 101
const multiGroupBy = (array, group, ...restGroups) => {
if(!group) {
return array;
}
const currGrouping = _.groupBy(array, group);
if(!restGroups.length) {
return currGrouping;
}
return _.transform(currGrouping, (result, value, key) => {
result[key] = multiGroupBy(value, ...restGroups);
}, {});
};
console.log(multiGroupBy([{x:1,y:1,z:1},{x:1,y:2,z:1},{x:2,y:1,z:1},{x:2,y:2,z:1},{x:1,y:1,z:2},{x:1,y:2,z:2},{x:2,y:1,z:2},{x:2,y:2,z:2}],'x','y'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
or if you prefer old syntax
function multiGroupBy(array, group) {
if(!group) {
return array;
}
var currGrouping = _.groupBy(array, group);
var restGroups = Array.prototype.slice.call(arguments);
restGroups.splice(0,2);
if(!restGroups.length) {
return currGrouping;
}
return _.transform(currGrouping, function(result, value, key) {
result[key] = multiGroupBy.apply(null, [value].concat(restGroups));
}, {});
}
console.log(multiGroupBy([{x:1,y:1,z:1},{x:1,y:2,z:1},{x:2,y:1,z:1},{x:2,y:2,z:1},{x:1,y:1,z:2},{x:1,y:2,z:2},{x:2,y:1,z:2},{x:2,y:2,z:2}],'x','y'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Upvotes: 2
Reputation: 51
const result = _.chain(vehicles)
.filter('selected')
.groupBy('makeCode')
.mapValues(values => _.chain(values)
.groupBy('modelCode')
.mapValues(_.size)
.value()
)
.value()
Upvotes: 5
Reputation: 867
Since you're already using lodash, you can take advantage of the _.filter function. This will return only the items where selected
is true.
var selectedVehicles = _.filter(response.vehicleTypes, 'selected');
Now that you have the selectedVehicles
array, you can use your original code for grouping by the makeCode
.
selectedVehicles = _.groupBy(selectedVehicles, function(item) {
return item.makeCode;
});
This returns an object, so we will need to iterate through those keys, and perform our second groupBy
_.forEach(selectedVehicles, function(value, key) {
selectedVehicles[key] = _.groupBy(selectedVehicles[key], function(item) {
return item.modelCode;
});
});
From this you will have an object of the form. I'll leave it to you to get the count from each array.
{ 'Make-A': { 'Model-a': [ ... ] },
'Make-B': { 'Model-c': [ ... ] },
'Make-C': { 'Model-b': [ ..., ... ] } }
Upvotes: 33