My Object groupBy.Food looks like
[
Object
amount: "15.0"
category: Object
debit: true
__proto__: Object
,
Object
amount: "10.0"
category: Object
debit: true
__proto__: Object
,
Object
amount: "11.1"
category: Object
debit: true
__proto__: Object
]
All I want is sum of amount in each object. I am using Lodash reduce as
var s = _.reduce(groupBy.Food, function(s, entry){
return s + parseFloat(entry.amount);
});
When I see value of s I get
s
"[object Object]1011.1"
What is that I am not doing right here?
By default, reduce starts out with the first two items in the list, so s will be the first item in the array and entry will be the second item the first time your function is called. Give it a value to start with:
var s = _.reduce(groupBy.Food, function(s, entry) {
return s + parseFloat(entry.amount);
}, 0);
(Array’s reduce behaves the same way.)
You can also use _.sum(array) or _.sumBy(array, [iteratee=_.identity]) for summing up for both array and object. See below examples.
_.sum([4, 2, 8, 6]); // ➜ 20
var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
_.sumBy(objects, function(o) { return o.n; }); // ➜ 20
_.sumBy(objects, 'n');// ➜ 20
Related
Lets say I have this array of objects:
const arrayOfObjects = [
{ task: "work", time: 1 },
{ task: "travel", time: 4 },
{ task: "work", time: 5 },
{ task: "eat", time: 3 },
{ task: "eat", time: 1 },
{ task: "eat", time: 5 }
];
and I want to return a single objects that returns each key as the task and each value as the sum of all values of the key.
for example, the produced object of the array above should be:
sumOfObejcts = {
work: 6,
travel: 4,
eat: 9
}
How can I do it properly with reduce function?
I don't know how to sum all the items of specific key, this is what I made after few tries from examples:
const sumOfObejcts = arrayOfObjects.reduce((acc, items) => {
let { task, time } = items;
return { ...acc, [task]: [...(acc[task] || []), time] };
}, {});
and the output I get is:
{
work: [1, 5],
travel: [4],
eat: [3, 1, 5]
}
So, I just want to return the sum of occurrence of the value instead.
Your solution is very close; except that you're creating array of values on each iteration, instead of summing the current time values.
Here I've changed the value you assign to the key by using a ternary statement. This checks to see if the task exists in the accumulator object; if the task already exists, that means there is already a sum for this task and thus we just need to add on the current time to the existing sum. Otherwise, if the accumulator object doesn't have the task, the value will be primed using the current task's time.
const sumOfObjects = arrayOfObjects
.reduce((acc, item) =>
({ ...acc, [item.task]: (
acc[item.task] // does the task exist in the accumulator object?
? acc[item.task] + item.time // if so, set a value equal to the current task's time plus the existing value
: item.time // otherwise, prime the task's value to the current time
) })
, {});
Use forEach and build the object
const sumOfObjects = (arr, all = {}) => (
arr.forEach(({ task, time }) => (all[task] = (all[task] ?? 0) + time)), all
);
const arrayOfObjects = [
{ task: "work", time: 1 },
{ task: "travel", time: 4 },
{ task: "work", time: 5 },
{ task: "eat", time: 3 },
{ task: "eat", time: 1 },
{ task: "eat", time: 5 },
];
console.log(sumOfObjects(arrayOfObjects));
I am trying to solve the problem in which i have to apply multiple filters to the array of object. Let suppose I am having a larger array of object which contains the configuration property which is further an object. On other side i have small object which are the ones the user chooses to filter(based on the checkboxes). i want to compare objects made with the parent array of objects by selecting multiple values.
So in the image the user chooses multiple values(using check boxes) and based on that he needs to filter the main array of objects.So after checking the checkboxes i get childObject and i have to filter parentArray on the basis of that..... please help me with this:
childobject =
{'Bathroom': '[2,1]',
'Bedroom': '[3,2]',
'halfBathroom':'0',
'name':'[2BD-2BA,2BD-2BA-1]'}
parentArray = [
0:{},
1:{},
2:{
'property1':'____',
'property2':'_____',
'configuration':'{
bathroom: 2
bedroom: 2
created_at: "2019-03-08 20:52:52"
created_by: 264
half_bathroom: 1
id: 26
is_selected: 0
name: "2BD-2BA-1/2BA"
name_en: "2BD-2BA-1/2BA"
name_es: "2RE-2BA-1/2BA"
status: 1
updated_at: "2019-08-23 05:39:44"
}'
}
3: {},
4:{}
]
I had to update the datastructure at some points:
You had different key in child and parent (upper/lowercase + camelcase/_ writing)
Some Missing } in the parent.
In child quotationmarks for integer deleted.Missing , added.
Changing some values in cruiteria, so that there is a result.
In parent delting of 0:, 1:, 2:, 3:, 4: to get a valid array.
childArray = {
'bathroom': [2,1],
'bedroom': [3,2],
'half_bathroom':1,
'name':['2BD-2BA', '2BD-2BA-1/2BA']
};
parentArray = [
{},
{},
{
'property1':'____',
'property2':'_____',
'configuration':{
bathroom: 2,
bedroom: 2,
created_at: "2019-03-08 20:52:52",
created_by: 264,
half_bathroom: 1,
id: 26,
is_selected: 0,
name: "2BD-2BA-1/2BA",
name_en: "2BD-2BA-1/2BA",
name_es: "2RE-2BA-1/2BA",
status: 1,
updated_at: "2019-08-23 05:39:44"
},
},
{},
{}
]
let res = parentArray.filter(elem => Object.entries(childArray).every(([key,val]) => {
let conf = elem.configuration;
if (conf===undefined) return false;
if (typeof(val) === 'object') {
return val.some(crit => crit===conf[key]);
} else {
return val===conf[key];
}
}));
console.log(res);
I have a result of the following code
d3.csv("../hello.csv", function(data) {
console.log(data);
var usable = data.map(function (d) {
return {
description: d.description.split(" ")
};
});
console.log(usable);
});
the console log of the usable is [object, object, object] when I expand it is
0:Object
description: Array[3]
0 : "A"
1 : "B"
2 : "C"
1:Object
description: Array[3]
0 : "D"
1 : "E"
2 : "FG"
2:Object
description: Array[5]
0 : "AD"
1 : "BD"
2 : "DC"
4 : "HH"
What I need is single array as follows:
[A,B,C,D,E,FG,AD,BD,DC,HH]
with all elements reduced to single array rather than having multiple.
How can I achieve this? Any help would be highly appreciated.
You can flatten the array using D3.merge(). Assume these are your objects:
var obj1 = {description: [1,2,3]}
var obj2 = {description: [10,20,30,40]};
var obj3 = {description: [100,200,300,400,500]};
var array = [obj1, obj2, obj3]; // your array
So this produces the output you described:
console.log("array", array);
You can use d3.merge(array) to merge several arrays into one:
var flattened = d3.merge([obj1.description, obj2.description, obj3.description]);
console.log("flattened", flattened);
It will print:
flattened [1, 2, 3, 10, 20, 30, 40, 100, 200, 300, 400, 500]
See: JSFiddle
Why not:
d3.csv("../hello.csv", function(data) {
console.log(data);
var all = [];
data.map(function (d) {
Array.prototype.push.apply(all, d.description.split(" "));
});
console.log(all);
});
I want to use linq.js to group the following data by date.
data2 = [{
"date": 1399298400.0,
"adId": 1057946139383,
"impressions": 1000000
}, {
"date": 1399298400.0,
"adId": 3301784671323,
"impressions": 535714
}...... etc.
];
Here's my attempt:
var linq = Enumerable.From(data2);
data2 = linq.GroupBy(function (x) {
return x.date;
}).Select(function (x) {
return {
date: x.Key(),
impressions: x.Sum(function (y) {
return y.impressions | 0;
})
};
}).ToArray();
However, It's not working correctly because the sum of all the impressions before and after the GroupBy are close but not identical.
What is the correct way to use group by in linq.js in this case?
Here's an example in fiddle with full dataset here which alerts the total impressions before and after using the GroupBy.
Solution
You can do this by passing a callback as the third parameter like this:
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return {
person: key,
likes: g.sum("$.likes | 0")
}
}).toArray()
Explanation
In groupBy, the third parameter allows you to modify the results before emitting:
In JS, the bitwise or operator (a single pipe |) returns the first value if it exists, otherwise it returns the second one. Without it, trying to sum an undefined value with a real one, will return NaN
undefined + 1 // NaN
Without | 0, the result would look like this:
This example uses shorthand syntax, but if you prefer anytime you see a string with a dollar sign, you can replace it with the lambda syntax like this (they both do the same exact thing):
// Shorthand
.Select("$.impressions")
// Lambda
.Select(function (x) { return x.impressions })
Working demo with Stack Snippets:
var dataArray = [
{
person: "james",
likes: 100
},
{
person: "james",
likes: 250
},
{
person: "kyle",
likes: 300
},
{
person: "kyle"
//,likes: 450
}
];
var grouped = Enumerable.from(dataArray).groupBy("$.person", null, (key, g) => {
return { person: key, likes: g.sum("$.likes | 0") }
}).toArray()
console.log(grouped);
<script src="https://unpkg.com/linq#3.2.0/linq.js"></script>
Further Reading:
SO - linqjs group by with a sum
I am the author of the open source project http://www.jinqJs.com. You can easily do that in jinqJs like this:
jinqJs().from(data2).groupBy('date').sum('impressions').select();
Let me know if I can be of anymore help.
You might try to group by date.toString(). Might be safer due to how JS evaluates dates equality
Alternatively, people coming into this question might have zero to a lot of buy in using linq.js.
If you're already pulling it in, go for it, but if this is the first couple real use cases for it, it's worth noting that you can accomplish the same thing in vanilla js:
For this data:
var dataArray = [
{ person: "james", likes: 100 },
{ person: "james", likes: 250 },
{ person: "kyle", likes: 300 },
{ person: "kyle" }
];
You can build an object with properties for each key / person and keep summing the values of those props
var obj = dataArray.reduce((acc, cur) => {
acc[cur.person] = (acc[cur.person] || 0) + (cur.likes || 0)
return acc
}, {})
If you want that to be an array of objects, you can convert from an object to array like this
var array = Object.entries(obj).map(entry => {
return { person: entry[0], likes: entry[1] }
})
In the following code:
function by(param) {
return function(a,b) {
if (parseInt(a[param]) < parseInt(b[param]))
return -1;
if (parseInt(a[param]) > parseInt(b[param]))
return 1;
return 0;
}
}
var data = [{
dogs: 3,
cats: 2,
fish: 14
},{
dogs: 30,
cats: 5
},{
dogs: 7,
cats: 8,
fish: 1
},{
dogs: 0,
cats: 8
}];
console.log(data.sort(by("fish")));
You can see that not all objects have all properties. What i need to do is to sort these objects in an order which first have the selected parameter values sorted by their order, and then the rest however, the output received is:
0: Object
cats: 2
dogs: 3
fish: 14
__proto__: Object
1: Object
cats: 5 //this shouldn't be here since it doesn't contain 'fish'
dogs: 30
__proto__: Object
2: Object
cats: 8
dogs: 7
fish: 1
__proto__: Object
3: Object
cats: 8
dogs: 0
now i get that there's something happens when the property is undefined, however i'm not sure as to how to deal with it.
Fiddle: http://jsfiddle.net/wffNn/
This should work: http://jsfiddle.net/wffNn/1/. I used substraction instead of if since it's faster and cleaner IMO
function by(param) {
return function(a,b) {
var vala = typeof a[param] == 'undefined' ? Infinity : parseInt(a[param]);
var valb = typeof b[param] == 'undefined' ? Infinity : parseInt(b[param]);
return vala-valb;
}
}
You can use Infinity if you want missing properties at the end, or -Infinity if you want them at the start.
You can use vala-valb if you want increasing order or valb-vala for decreasing order.
You can mix the previous two statements to get the kind of sorting you want.
This can be written quite idiomatically using this little helper function:
function cmp(x, y) { return x > y ? 1 : x < y ? -1 : 0 }
and then
function by(param) {
return function(a, b) {
return cmp(param in b, param in a) || cmp(a[param], b[param]);
}
}