I've got the following array
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
Expected output: 9
And I would like to sum the properties the shortest way possible. The thing is that the object keys are always variable so i can't go for:
arr.reduce((acc,item) => acc+item.keyName)
Found this post but can't adapt the code to my solution either:
var data = [{ A: 4, B: 2 }, { A: 2, B: 1 }, { A: 3, B: 1 }, { A: 2, B: 1, C: 1 }],
result = data.reduce((r, o) => (Object.entries(o).forEach(([k, v]) => r[k] = (r[k] || 0) + v), r), {});
Thank you in advance
Here's my solution. Map through the array and flatten the properties values, then reduce them.
const arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
console.log(arr.flatMap(e => Object.values(e)).reduce((a, b) => a + b));
Use reduce twice, once on the outer array and once on the values of each object inside it.
const arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
];
const total = arr.reduce((gt, item) =>
gt + Object.values(item).reduce((t, sub) => t + sub, 0)
, 0);
console.log(total);
Maybe something like this?
let acc = {};
for (let o of arr) for (let key in o) {
acc[key] ??= 0;
acc[key] += o[key];
}
Not a one-liner though
You can do something like this:
total = arr.reduce((acc,item) => {
const values = Object.values(item)
values.forEach(item=> acc += item)
return acc;
}, 0);
Check I passed the first value (0) as the second parameter to reduce method.
Hope this would help:
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
var sum = arr.map(item => {
var x = 0
for (const [key, value] of Object.entries(item)) {
x+=value
}
return x
}).reduce( (acc, curr) => acc + curr)
console.log(sum)
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
var total = 0;
arr.forEach((x, i) =>
total += Object.values(x).reduce((a,b) => a+b)
);
console.log(total)
Solve it with this approach:
use JSON.stringify to get arr as string.
extract numbers with match with regex \d+ and flag g to return it as group.
map it to real numbers rather than string.
reduce it to the sum of all numbers.
JSON.stringify(arr) //'[{"Atencion Personalizada":2,"Caja":3},{"Atencion Personalizada":1},{"Tarifa Social":3}]'
.match(/\d+/g) //['2', '3', '1', '3']
.map(Number) //[2, 3, 1, 3]
.reduce((acc, n)=>acc+n, 0) //9
Related
Sorry, in advance if the title is unclear, but it's hard to describe it in a words.
What I have:
const obj = {
a: 5,
b: 3,
c: 0,
d: 9
}
What I want to have:
const arr = [[a, 5] ,[b, 3]]
Basically, I try to write a function that return me array of entries, but it has too meet requirements:
don't want objects when values is equal to 0
sum of values must be less than 10
First point is easy for me and I can do it by
Object.entries(obj).filter(([k, v])=> v !== 0)
but I can't handle with the second one.
May I use reduce here?
You can use a closure and an IIFE to store the sum
Object.entries(obj).filter((() => {
let sum = 0;
return ([k, v]) => { sum += v; return v !== 0 && sum < 10; };
})());
Examples:
function convert(obj) {
return Object.entries(obj).filter((() => {
let sum = 0;
return ([k, v]) => { sum += v; return v !== 0 && sum < 10; };
})());
}
const obj = { a: 5, b: 3, c: 0, d: 9 };
const arr = convert(obj);
console.log(arr);
const obj2 = { a: 0, b: 0, c: 8, d: 0, e: 1, f: 5 };
const arr2 = convert(obj2);
console.log(arr2);
const obj3 = { a: 12 };
const arr3 = convert(obj3);
console.log(arr3);
#jabaa's answer is great and you should accept it.
Just to confirm your intuition, you could have used reduce, but it would get rather complicated:
const obj = {
a: 5,
b: 3,
c: 0,
d: 9
}
const result = Object.entries(obj).reduce(
(o, newPair) => {
o.sum += newPair[1];
newPair[1] !== 0 && o.sum < 10 && o.pairs.push(newPair);
return o;
},
{
sum: 0,
pairs: []
}
).pairs;
console.log(result)
What is the best way to merge all objects in my array to one and addition all value with the same key?
I tried to achieve this in es6 with the spread operator, but I have no success so far...
const array = [{
on: 1,
off: 1,
alarm: 1,
},{
on: 1,
off: 1,
alarm: 1,
},{
on: 1,
off: 1,
alarm: 1,
}];
const output = [{
on: 3,
off: 3,
alarm: 3,
}];
Another way, using the spread operator as you suggested:
const array = [
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 }
];
const output = array.reduce((res, o) => ({
...res,
...Object.fromEntries(
Object.entries(o).map(([k, v]) => [k, v + (res[k] || 0)])
)
}), {});
console.log(output);
Reduce the array, iterate each object entries with Array.forEach(), and assign / add to the respective key on the accumulator (acc):
const array = [{"on":1,"off":1,"alarm":1},{"on":2,"off":2,"alarm":2},{"on":3,"off":3,"alarm":3}];
const result = array.reduce((acc, o) => {
Object.entries(o).
forEach(([k, v]) => acc[k] = (acc[k] ?? 0) + v)
return acc;
}, {});
console.log(result);
OK, here is my two-pennies-worth:
const arr = [{ on: 1, off: 1, alarm: 1},
{on: 1,off: 1,alarm: 1},
{on: 1,off: 1,alarm: 1}];
const res={};
arr.forEach(c=>Object.entries(c).forEach(([k,v])=>res[k]=(res[k] || 0) + v) )
console.log(res)
array.reduce((acc, elem) => {
Object.keys(elem).forEach(key => {
if (acc[key]) {
acc[key] += elem[key];
} else {
acc[key] = elem[key];
}
});
return acc;
}, {})
will return an object {on: 3, off: 3, alarm: 3}
Other solutions appear more elegant, but this was the simplest for me to understand.
const array = [
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 }
];
let output = { 'on':0, 'off':0, 'alarm':0 }
array.forEach( (elem,ndx) => { // console.log(ndx, elem)
output.on += elem.on;
output.off += elem.off;
output.alarm += elem.alarm;
});
console.log(JSON.stringify(output,null,2));
I have this array of objects:
let foo = [
{
num: 1,
value: 0.5
},
{
num: 1,
value: 1.5
},
{
num: 2,
value: 0.5
},
]
How can I reduce this array to return:
let bar = [
{
num: 1,
value: 2, // 1.5 + 0.5
},
{
num: 2,
value: 0.5
}
]
You can use findIndex to get the object where num is same
let foo = [{
num: 1,
value: 0.5
},
{
num: 1,
value: 1.5
},
{
num: 2,
value: 0.5
},
];
let newData = foo.reduce((acc, curr) => {
let findNumIndex = acc.findIndex(elem => elem.num === curr.num);
if (findNumIndex !== -1) {
acc[findNumIndex].value += curr.value;
} else {
acc.push({...curr})
}
return acc;
}, []);
console.log(newData)
You can use .reduce as follows:
let foo = [
{
num: 1,
value: 0.5
},
{
num: 1,
value: 1.5
},
{
num: 2,
value: 0.5
},
];
var helper = {};
let arr = foo.reduce(function(r, o) {
var key = o.num;
if(!helper[key]) {
helper[key] = Object.assign({}, o);
r.push(helper[key]);
} else {
helper[key].value += o.value;
}
return r;
}, []);
console.log(arr);
Using reduce & Object.assign() like below you can get desired result. Explanation is added as comment in code.
Note I used Object.assign as a.push(Object.assign({}, x)); instead of a.push(x); because with a.push(x); later when we update value while finding existing object it will also update value of foo. Using Object.assign({}, x); this will not happen.
let foo = [{
num: 1,
value: 0.5
},
{
num: 1,
value: 1.5
},
{
num: 2,
value: 0.5
},
];
// use reduce to iterate over array and produce result
var bar = foo.reduce((a, x) => {
// find matching object from a.
let obj = a.filter(y => y.num == x.num)[0];
// if not exists then create new object and add into a
// else add value to obj.value
if (!obj) {
a.push(Object.assign({}, x));
} else {
obj.value += x.value;
}
// return a
return a;
}, []);
console.log(bar);
You could simply sum all the values together in an object, and then afterwards reconstruct the required data
let foo = [
{
num: 1,
value: 0.5
},
{
num: 1,
value: 1.5
},
{
num: 2,
value: 0.5
},
];
// sum all values first
const summedMap = foo.reduce( (agg, cur) => (agg[cur.num] = (agg[cur.num] || 0) + cur.value, agg), {});
// reconstruct afterwards
console.log( Object.entries( summedMap ).map( ([num,value]) => ({ num: parseInt(num), value: value }) ) );
I have problem making a new array of object. I want to transform this
[{
a: 1,
b: true
},{
a: 2,
b: false
}]
to
[{
a_1: 1
},{
a_2: 2
}]
I tried map
const result = a.map((o, i) => {
let row = []
i = ++i
row = {
[`a_${i}`]: o.a,
[`b_${i}`]: b.a
}
return row
})
but it returned this
[
{
"a_1": 1,
"b_1": true
},
{
"a_2": 2,
"b_2": false
}
]
How do I get this
[
{
"a_1": 1,
},{
"b_1": true
},{
"a_2": 2,
},
{
"b_2": false
}
]
I can flatten it but the property key has dynamic index, imagine it's not small size like this.
You can use map and Object.entries and flat
let arr = [{ a: 1, b: true }, { a: 2, b: false}]
const result = arr.map((o, i) => {
return Object.entries(o).map(([key, value]) => ({
[key + '_' + (i + 1)]: value
}))
}).flat()
console.log(result)
Also you can use Array.flatMap
let arr = [{ a: 1, b: true }, { a: 2, b: false}]
const result = arr.flatMap((o, i) => {
return Object.entries(o).map(([key, value]) => ({
[key + '_' + (i + 1)]: value
}))
})
console.log(result)
And you can use reduce:
let arr = [{a: 1,b: true},{a: 2,b: false}];
let brr= arr.reduce((acc,e, i)=>{
Object.entries(e).map(([key,value])=>{
acc.push({[`${key}_${i+1}`]:value})
});
return acc
},[])
console.log(brr)
I'm struggling a bit with implementing a variant groupBy that would allow grouping by multiple properties in a point-free style. (I'm using typescript & ramda).
I want to group some elements say of type A by properties returned from function getProperties :: A a -> [b]. In imperative paradigm the implementation could look like that:
const getProperties = (item: Item): Array<keyof Item> => [item.x, item.y.z];
const groupByMany = (items: Item[]): Dictionary<Item[]> => {
let groupped: Dictionary<Item[]> = {};
for (let item of items) {
for (let key of getProperties(item)) {
if (groupped[key]) {
groupped[key].push(item);
} else {
groupped[key] = [item];
}
}
}
}
Example:
const items = [
{ i: 1, x: 'A', y: { z: 'B' } },
{ i: 2, x: 'A' },
{ i: 3, x: 'B', y: { z: 'B' } },
];
const expectedOutput = {
A: [ { i: 1, ... }, { i: 2, ... }],
B: [ { i: 1, ... }, { i: 3, ... }],
};
I'll get you started -
const reduce = (f, init, xs) =>
xs .reduce (f, init)
const upsert = (m, k, v) =>
m .has (k)
? m .get (k) .push (v)
: m .set (k, [ v ])
const groupByMany = (f, xs) =>
reduce
( (m, x) =>
( f (x) .forEach (k => k && upsert (m, k, x))
, m
)
, new Map
, xs
)
const items =
[ { i: 1, x: 'A', y: { z: 'B' } }
, { i: 2, x: 'A' }
, { i: 3, x: 'B', y: { z: 'B' } }
]
const result =
groupByMany
( item => [ item.x, item.y && item.y.z ]
, items
)
console.log(Object.fromEntries(result.entries()))
Notice how the last item has a B for .x and .y.z so it get's inserted into the B group twice. We change upsert so it will not insert a duplicate value -
const upsert = (m, k, v) =>
m .has (k)
? m .get (k) .includes (v)
? m
: m .get (k) .push (v)
: m .set (k, [ v ])
Expand the snippet below to see the final result in your own browser -
const reduce = (f, init, xs) =>
xs .reduce (f, init)
const upsert = (m, k, v) =>
m .has (k)
? m .get (k) .includes (v)
? m
: m .get (k) .push (v)
: m .set (k, [ v ])
const groupByMany = (f, xs) =>
reduce
( (m, x) =>
( f (x) .forEach (k => k && upsert (m, k, x))
, m
)
, new Map
, xs
)
const items =
[ { i: 1, x: 'A', y: { z: 'B' } }
, { i: 2, x: 'A' }
, { i: 3, x: 'B', y: { z: 'B' } }
]
const result =
groupByMany
( item => [ item.x, item.y && item.y.z ]
, items
)
console.log(Object.fromEntries(result.entries()))
A note on SO's peculiar output: SO will not display the same object twice, instead it will give an object a reference, and print that reference where the duplicate object would appear. For example /**id:3**/ in the program's output -
{
"A": [
{
/**id:3**/
"i": 1,
"x": "A",
"y": {
"z": "B"
}
},
{
"i": 2,
"x": "A"
}
],
"B": [
/**ref:3**/,
{
"i": 3,
"x": "B",
"y": {
"z": "B"
}
}
]
}
Which matches your expected output -
const expectedOutput = {
A: [ { i: 1, ... }, { i: 2, ... }],
B: [ { i: 1, ... }, { i: 3, ... }],
};
It's not point-free like you asked for, but I only said I'd get you started ...
I couldn't tell from the question whether you wanted something that made it easy for you to code point-free or if for some reason you were looking for an actual point-free implementation. If it's the latter, then I'm afraid this will be no help. But it's a fairly simple
const groupByMany = (fn) => (xs) =>
xs .reduce
( (a, x) => [...new Set ( fn(x) )] . reduce
( (a, k) => k ? {...a, [k]: [... (a [k] || []), x ] } : a
, a
)
, {}
)
// const getProperties = (item) => [path(['x'], item), path(['y', 'z'], item)]
const getProperties = juxt ( [path (['x']), path (['y', 'z']) ] )
const items = [{ i: 1, x: 'A', y: { z: 'B' } }, { i: 2, x: 'A'}, { i: 3, x: 'B', y: { z: 'B' } }]
console .log
( groupByMany (getProperties) (items)
)
<script src="https://bundle.run/ramda#0.26.1"></script></script>
<script>const { juxt, path } = ramda </script>
Running the keys through [... new Set ( fn(x) ) ] is just a quick way to eliminate duplicates from the array returned by fn (x). The rest of the function should be pretty clear.