Lodash group by multiple properties if property value is true
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': [ ..., ... ] } }
Group array of objects by multiple properties with Lodash
You can just concat your year and month with groupBy and use it.
var grouped = _.groupBy(data, function(i) {
return new Date(i.date).getFullYear()+'-'+new Date(i.date).getMonth()
})
var resultUnsorted = _.map(t, (val, key) => ({key: key, val: val.reduce((p, c) => c.amount + p, 0) }));
then sort using _.orderBy
const output = _.orderBy(resultUnsorted, 'key');
you can write your custom sort function using the behaviour you want.
Grouping objects by multiple columns with Lodash or Underscore
A solution using underscore:
var props = ['userId', 'replyToId'];
var notNull = _.negate(_.isNull);
var groups = _.groupBy(record.notes, function(note){
return _.find(_.pick(note, props), notNull);
});
lodash Groupby based on multiple conditions
If you are sure that your object structure will be exactly the same always and will have only one object in details
then you can directly do Stringify to identify the combo, if your object have several data, then you can create a new object {age: value, street: value}
and stringify it to identify the combo. However I will strongly suggest not to use any pipe or anything as deliminator, and joining them to create unique combination of string to find a group. Sometimes that can spoil your value.
Here is what you need:
_(rawData).groupBy(JSON.stringify).filter(arr=>arr.length>1).mapKeys(v=>v[0].details[0].address).value()
Here is an working example:
var rawData = [{ age: "12", details: [ { address: "street1" } ]},{ age: "12", details: [ { address: "street2" } ]},{ age: "12", details: [ { address: "street1" } ]}];
var result = _(rawData) .groupBy(JSON.stringify) .filter(arr=>arr.length>1) .mapKeys(v=>v[0].details[0].address) .value(); console.log('Expected result: ', result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
How to group array with multiple properties
You've clarified that every item can only have one of the three properites is_sweet
/is_spicy
/is_bitter
equal to true
.
Given this assumption, one approach is that we can group the items based on the value of the quantity x.is_sweet*4 + x.is_spicy*2 + x.is_bitter*1
for the item x
. This quantity is equal to 1 for those who have only is_bitter: true
, 2 for item having only is_spicy: true
, and 4 for item having only is_sweet: true
.
Once you group according to that quantity, you can use _.values
to get rid of the keys created by _.groupBy
:
_.values(_.groupBy(foodsData, x => x.is_sweet*4 + x.is_spicy*2 + x.is_bitter*1))
Obviously you can give a descriptive name to the predicate:
const flavour = food => food.is_sweet*4 + food.is_spicy*2 + food.is_bitter*1;
let groupedFoods = _.values(_.groupBy(foodsData, flavour))
Notice that in case items are allowed to have more than one of those three keys true, the code above will still give sensible results, in the sense that you will have the eight groups relative to [is_sweet, is_spicy, is_bitter]
equal to [0,0,0]
, [0,0,1]
, [0,1,0]
, [0,1,1]
, [1,0,0]
, [1,0,1]
, [1,1,0]
, and [1,1,1]
.
In hindsight, we don't need that the predicate flavour
pulls out a number for each entry food
; based on last paragraph, we are just happy if it pulls the three values food.is_sweet
, food.is_spicy
, and food.is_bitter
, and puts them in an array. Lodash has _.over
for this, so we could define
const flavour = _.over([is_sweet, is_spicy, is_bitter]);
A few words about the readability of the solution above.
_.groupBy
is a higher level abstraction thanreduce
-like (member or free) functions; in other words the latter functions are much more powerful than you need for the usecase in the question, and therefore they are less expressive than the former. Indeed,_.groupBy
could well be implemented in terms ofreduce
. Here's a brutal implementation to show that it is possible:const groupBy = (things, f) => {
return things.reduce((acc, item) => {
if (acc[f(item)] == undefined)
acc[f(item)] = [item];
else
acc[f(item)].push(item);
return acc;
}, {});
}
// both these give the same result:
groupBy([1,2,3,4,4,4], x => x % 2 == 0); // mine
_.groupBy([1,2,3,4,4,4], x => x % 2 == 0); // lodash'sThe solution is textually extremely short: it doesn't matter how "complicate" or "fancy" it looks; once you get used to this way of doing things (aka the right way), you end up reading the code. Look at it again:
_.values(_.groupBy(foodsData, flavour))
That's already fairly readable, but if you use
lodash/fp
module, you can even improve it:_.values(_.groupBy(flavour, foodsData))
How far is that from the following English sentence?
"get the values of the result of grouping by flavor the foods"
Saying that's not readable just means denying the reality of things.
Longer solutions using lower lever constructs like
for
loops or (a bit better)reduce
-like utilities force you to parse the code, instead. There's no way you understand what those solutions do without carefully inspecting their bodies. Even if you decided to pull the function(acc, item) => { … }
out of the call toreduce
and give it a name, ending up withinput.reduce(fun, acc)
how would you even name it? It is a function which pushes elements on top of the arrays in an intermediate result. Except in trivial use cases, you're not gonna find a good name for such a mouthful. You're just moving the problem somewhere else. People reading this code will still be puzzled._.over
is maybe a bit scary too:const flavour = _.over([is_sweet, is_spicy, is_bitter]);
But really, what does it cost tha you just learn and accept what
_.over
means? I mean, it is just like learning a new language, nothing more. And the explanation of_.over
is fairly simple:_.over([iteratees=[_.identity]])
Creates a function that invokes
iteratees
with the arguments it receives and returns their results.It is so simple:
_.over(['a', 'b', 'd'])({a: 1, b: 2, c: 3}) == [1, 2, undefined]
Multilevel group by using lodash
You could chain groupBy
and map
methods to first group by key, address, and className and then do one more groupBy
to group and count elements with unique deviceId
const array = [{"key":"11111","address":{"city":"NY","country":"USA"},"className":"google.com","deviceId":"aaaaa"},{"key":"11111","address":{"city":"NY","country":"USA"},"className":"google.com","deviceId":"aaaaa"},{"key":"33333","address":{"city":"NY","country":"USA"},"className":"facebook.com","deviceId":"aaaaa"},{"key":"11111","address":{"city":"NY","country":"USA"},"className":"google.com","deviceId":"ddddd"},{"key":"22222","address":{"city":"Landon","country":"UK"},"className":"stackoverflow.com","deviceId":"ccccc"},{"key":"22222","address":{"city":"Landon","country":"UK"},"className":"stackoverflow.com","deviceId":"ggggg"},{"key":"22222","address":{"city":"Landon","country":"UK"},"className":"stackoverflow.com","deviceId":"fffff"}]
const result = _.chain(array) .groupBy(({ key, address: { city, country }, className }) => { return `${key}-${city}-${country}-${className}` }).map(e => { const [{ key, address, className }] = e; return { keys: { key, address, className }, count: _.keys(_.groupBy(e, 'deviceId')).length } })
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
Group js objects by multiple properties
Is this what you are looking for ?
var arr = [{ "id": 1, "tags": "main", "yearCode": "2018" }, { "id": 2, "tags": ["main", "blue"], "yearCode": "2018" }, { "id": 3, "tags": ["main", "green"], "yearCode": "2018" }, { "id": 25, "tags": ["green"], "yearCode": "2018" }, { "id": 26, "tags": ["important"], "yearCode": "2017" }, { "id": 29, "tags": ["important", "blue"], "yearCode": "2017" }, { "id": 2, "tags": ["important", "green"], "yearCode": "2017" }];
var mainFilter = "yearCode"; var secFilter = "tags"; var result = arr.reduce(function(map, obj) {
var f1 = map[obj[mainFilter]] = map[obj[mainFilter]] || {};
if(Object.prototype.toString.call(obj[secFilter]) === '[object Array]') { var idx; for(idx in obj[secFilter]) { var f2 = f1[obj[secFilter][idx]] = f1[obj[secFilter][idx]] || []; f2.push(obj); } } else { var f2 = f1[obj[secFilter]] = f1[obj[secFilter]] || []; f2.push(obj); }
return map; }, Object.create(null));
console.log(JSON.stringify(result));
Using Lodash to GroupBy a specific value in another object
You could do this in relatively concise manner with ES6 and Array.reduce
:
const group = arr => arr.reduce((r,{a,b}) => { if(!r[a] && !r[b]) r[a] = r[b] = {a,b} else { if(r[a]) r.grp[a] = [...r.grp[a] || [r[a]], {a,b}] if(r[b]) r.grp[b] = [...r.grp[b] || [r[b]], {a,b}] } return r}, {grp: {}}).grp
console.log(group([{ a: 88, b: 11, }, { a: 99, b: 88 }, { a: 22, b: 10 }]))console.log(group([{ a: 88, b: 11, }, { a: 99, b: 88 }, { a: 22, b: 10 }, { a: 22, b: 88 }]))
Group objects by multiple properties in array then sum up their values
Use Array#reduce with a helper object to group similar objects. For each object, check if the combined shape
and color
exists in the helper. If it doesn't, add to the helper using Object#assign to create a copy of the object, and push to the array. If it does, add it's values to used
and instances
.
var arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];
var helper = {};var result = arr.reduce(function(r, o) { var key = o.shape + '-' + o.color; if(!helper[key]) { helper[key] = Object.assign({}, o); // create a copy of o r.push(helper[key]); } else { helper[key].used += o.used; helper[key].instances += o.instances; }
return r;}, []);
console.log(result);
lodash groupby and sum object properties
How about the following ES6 only solution:
const list = [
{ property: 'foo', value: 1 },
{ property: 'foo', value: 3 },
{ property: 'bar', value: 2 },
{ property: 'bar', value: 3 },
{ property: 'chu', value: 8 },
];
const result = list.reduce((obj, el) => {
obj[el.property] = (obj[el.property] || 0) + el.value;
return obj;
}, {});
console.log(result);
Related Topics
How to Iterate Through Json Nested Objects With Ngfor
Jest Encountered an Unexpected Token: Syntaxerror: Unexpected Token {
React Passing Import Name as Prop and Adding to Image Src
Jquery Wait Until Ajax Call Has Got Data Before Displaying
Discord.Js Ban/Kick Commands Available to All Users. How to Fix This
Jquery Click Events Firing Multiple Times
Reactjs: Expected Onclick Listener to Be a Function, Instead Got Type String
How to Get the Index from a Json Object With Value
Best Way to Show a Loading Spinner/Gif While My React Component Is Fetching Via Ajax
How to Use Zindex in React-Native
Converting Text to Unicode in JavaScript
Delete Span Tag Along With Text in HTML Permanently
How to Close Sweet Alert on Ajax Request Completion
Trying to Add Image to Pdf Using Jspdf
How to Mock a Fake Database for When Unit Testing Against Knex
React-Native React-Navigation Undefined Is Not an Object (Evaluating 'S.Value.Startswith')