Lodash. How to get aggregate array from array objects - javascript

For example, I have an array:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
How to get an array values from reference like
const result = [0, 10, 20, 25];
First step always = 0
Second step 0 + 10 = 10
Third step 0 + 10 + 10 = 20
Forth step 0 + 10 + 10 + 5 = 25

You can reduce the array, and add the current value to the last sum:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
const result = reference
.reduce((r, o, i) => {
r.push(i === 0 ? 0 : r[r.length - 1] + o.value);
return r;
}, [])
console.log(result);

You could map the values by taking a closure over the sum and take zero for the first element.
const
reference = [{ id: 1, value: 10 }, { id: 2, value: 10 }, { id: 3, value: 10 }, { id: 4, value: 5 }],
result = reference.map((sum => ({ value }, i) => sum += i && value)(0));
console.log(result);

The way I would do this would be by using the Array.reduce method as follows :
let result = [0]
reference.reduce((acc, cur) => {
result.push(Object.values(cur)[1]+result[result.length-1])
})
Hope it helps

Related

Flatten whilst splitting into smaller objects

I am having these kind of array structure.
[{ "primary_product": "Blueberry",
"list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
I want to destructure the pattern and make it like this below
[{"id": 1,"value":"Blueberry"},{"id": 2,"value":"Raspberry"},{"id": 3,"value":"Strawberry"}, …]
Primary product will be the first product and then make the array of strings into key/value pair. How to do this using es6?
All you need is basic functions like forEach and push. I would recommend learning these.
let arr1 = [{ "primary_product": "Blueberry", "list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
arr2 = [{ id: 1, value: arr1[0].primary_product }]
arr1[0].list_of_products.forEach((element) => {
arr2.push({ id: arr2.length + 1, value: element })
})
Here's a one-liner using map on the list_of_products:
const arr = ['Raspberry','Strawberry','Blackberry'];
return arr.map((val, i) => {return {id: i+1, value: val}});
This is the result:
[
{ id: 1, value: 'Raspberry' },
{ id: 2, value: 'Strawberry' },
{ id: 3, value: 'Blackberry' }
]
Note that the callback to map includes (currentValue, index, arr).
To make things slightly easier for the eyes I've simplified the structure:
const p = [ { a: 100, b: [101, 102, 103]}
, { a: 200, b: [201, 202, 203]}];
You can flatten all the numbers into a single list i.e. [100, 101, 102, 103, 200, 201, 202, 203] with:
p.flatMap(({a, b}) => [a, ...b]);
To get closer to what you're after, let's first create an id function that will return the next number:
const id = (n => () => ++n)(0);
id(); //=> 1
id(); //=> 2
id(); //=> 3
// …
Then let's create a function obj that takes an x and wraps it into an object:
const obj => x => ({id: id(), value: x});
obj(100); //=> {id: 1, value: 100);
obj(200); //=> {id: 2, value: 200);
// …
Then you can do:
p.flatMap(({a, b}) => [obj(a), ...b.map(obj)]);
//=> [ {id: 1, value: 100}
//=> , {id: 2, value: 101}
//=> , {id: 3, value: 102}
//=> , {id: 4, value: 103}
//=> , {id: 5, value: 200}
//=> , {id: 6, value: 201}
//=> , {id: 7, value: 202}
//=> , {id: 8, value: 203}]

Find objects with same prop and return the update value with its associated prop [duplicate]

I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.
You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce
A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example
You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);
JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});
You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);
You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0

Compare two javascript array of objects and merge data

I'm having a bad time comparing two array of objects on a key.
I would like to compare, substract value when the key matches and display negative value when not in my target array. Finally, I want to have all target objects (if key didn't match) inside my final array.
An exemple would save 1000 words :
const initial = [{id: 1, value: 47}, {id: 2, value: 20}, {id: 7, value: 13}];
const target = [{id: 1, value: 150}, {id: 3, value: 70}, {id: 40, value: 477}];
//Desired output
// [{id: 1, value: 103}, {id: 2, value: -20}, {id: 7, value: -13}, {id: 3, value: 70}, {id: 40, value: 477}];
let comparator = [];
initial.map(initia => {
let hasSame = target.find(targ => {
return initia.id === targ.id
});
if(hasSame){
initia.value -= hasSame.value
} else{
initia.value = -initia.value
}
});
console.log(initial);
I'm getting almost the result I want except that I don't know how to merge target values properly. Is it possible to merge this values without looping over target array once more? Or could I do that inside the find ?
I want to get advice to do this as clean as possible
Thanks you!
You could use a Map and collect same id with a wanted factor for summing.
As result take key/value as new properties.
var add = (map, factor) => ({ id, value }) => map.set(id, map.has(id)
? map.get(id) - value * factor
: value * factor
),
initial = [ {id: 1, value: 47 }, { id: 2, value: 20 }, { id: 7, value: 13 }],
target = [{ id: 1, value: 150 }, { id: 3, value: 70 }, { id: 40, value: 477 }],
map = new Map,
result;
initial.forEach(add(map, -1));
target.forEach(add(map, 1));
result = Array.from(map, ([id, value]) => ({ id, value }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If the intent is to avoid a nested find inside of the loop, you could reduce the two arrays in to a Set. They don't allow duplicate keys, so you would ensure a single value for each ID provided.
const valueSet = [...initial, ...target].reduce((total, obj) => {
total[obj.id] = !total[obj.id]
? -obj.value
: total[obj.id] -= obj.value
return total;
}, {});
const result = Object.keys(valueSet).map(key => ({ id: key, value: valueSet[key]}))
console.log(result);
Then you'd map back over the result to build out the intended array.

How to sum value in javascript array object form specific search id?

I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.
You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce
A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example
You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);
JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});
You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);
You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0

JavaScript objects array filter by minimum value of an attribute

I need to filter this object array by minimum value of 'rest' attribute. This is an one way to do it. Is there any other ways ?
'data' variable is a result of chained function. Is there any other way to do this without calling 'data' variable again inside Math.min() function.
let data =
[ { size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 } ]
let result = data.filter(e=> e.rest === Math.min(...data.map(f=>f.rest) ) );
console.log(result);
// result is
//[ { size: 5, qty: 2, rest: 0 },
// { size: 2, qty: 5, rest: 0 },
// { size: 1, qty: 10, rest: 0 }]
The easiest way is to pull the min function out of the filter like this:
let min = Math.min(...data.map(item => item.rest))
This is much more efficient as we are no longer loop over the data to find the min for every iteration of the filter.
We now have n * 2 passes instead of n^2 passes. (n is the size of your data set, 5 in this case)
Full example below:
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
]
let min = Math.min(...data.map(item => item.rest))
let result = data.filter(item => item.rest === min)
console.log(result)
Hope this helps!
Lloyd
data.map inside of data.filter is O(N^2); for an O(N) solution, iterate through data ahead of time to calculate the minimum, then filter by that minimum:
let data =
[ { size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 } ];
const minRest = Math.min(...data.map(({ rest }) => rest));
let result = data.filter(({ rest }) => rest === minRest);
console.log(result);
imo. the simplest/best solution is the one #CertainPerformance gave you.
Just wanted to add another solution with linear runtime (that truly iterates only once over the Array)
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
];
let result = data.reduce((result, item) => {
let minRest = result.length? result[0].rest: item.rest;
if (item.rest < minRest) {
minRest = item.rest;
result.length = 0;
}
if (item.rest === minRest) {
result.push(item);
}
return result;
}, []);
console.log(result);
#mathieux51 got me another idea how you can do this inside a method chain, but the readability/clarity/intention is not as good as with the other approaches:
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
];
let result = data.sort((a, b) => a.rest - b.rest)
.filter((item, index, array) => item.rest === array[0].rest);
console.log(result);
It sounds like you want to sort the list. I would do it as following:
const result = data.sort((a, b) => a.rest - b.rest)
Get Min or Max
Since no one mentioned this method I will update it here.
myArray.sort(function (a, b) {
return a.rest - b.rest
})
var min = myArray[0],
max = myArray[myArray.length - 1]
It has good readability/clarity/intentions.

Categories