Besides the horrible name of the question my question is quite simple. I have this object:
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
That I want to transform to something like this:
let result = {
date1: {
values: [5, 6],
times: [1, 2],
},
date2: {
values: [1, 2], // easier to summarise?!
times: [10, 20],
},
};
I actually want to do this in order to summarise the value-values for each date. I thought that if I have them in an array it would be easier to summarise them. I know there are other forms to do this (and I'd be happy to see any solutions).
My current approach does not what I want it to do. It looks like this:
let keys = Object.keys(test);
let red = keys.reduce((acc, curr) => {
return (acc[curr] = test[curr].map((e) => e.value));
}, {});
console.log(`red: `, red);
And produces this:
red: [ 20, 10 ]
This
return (acc[curr] = test[curr].map((e) => e.value));
is equivalent to
acc[curr] = test[curr].map((e) => e.value);
return acc[curr];
going inside a nested key of the accumulator on every iteration - which isn't the logic you want. Return the whole accumulator on a separate line, so previously assigned values don't get lost, and you also need to account for both the time and value properties of the array being iterated over - your => e.value only extracts one of the two properties you want.
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
const keys = Object.keys(test);
const result = keys.reduce((acc, key) => {
acc[key] = {
values: test[key].map(({ value }) => value),
times: test[key].map(({ time }) => time),
};
return acc;
return acc;
}, {});
console.log(result);
or do
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
const result = Object.fromEntries(
Object.entries(test).map(([key, arr]) => [
key,
{
values: arr.map(({ value }) => value),
times: arr.map(({ time }) => time),
}
])
);
console.log(result);
Try modifying it a little like this:
let result = Object.keys(test).reduce((acc, key) => {
test[key].forEach((item) => {
acc.push({
date: key,
time: item.time,
value: item.value,
});
});
return acc;
}
, []);
console.log(result);
Assuming all inner objects have the same keys and no date array is empty:
let test = {date1:[{time:1,value:5},{time:2,value:6},],date2:[{time:1,value:20},{time:2,value:10},]};
let keys = Object.keys(test);
let red = keys.reduce((acc, curr) => ({
...acc,
[curr]: Object.keys(test[curr][0])
.reduce((acc, key) => ({
...acc,
[key + 's']: test[curr].map(o => o[key])
}), {})
}), {});
console.log(`red: `, red);
There is no need to first create arrays when you want to sum up values from different objects. It looks like you want to achieve this result:
{
date1: 11
date2: 30
}
The idea to use reduce is fine (for summing up values). You can use Object.entries and Object.fromEntries on top of that, in order to create the new object structure:
const test = {date1: [{time: 1,value: 5,},{time: 2,value: 6,},],date2: [{time: 1,value: 20,},{time: 2,value: 10,},],};
const result = Object.fromEntries(
Object.entries(test).map(([key, arr]) =>
[key, arr.reduce((sum, {value}) => sum + value, 0)]
)
);
console.log(result);
Related
I have an array of objects and inside the array there is another array of objects, I would like to sum the values. So the sum is based on the same picker_id. Then sum current_capacity, process_time_in_minutes, and picked_qty inside products which is array of objects.
Here is my data:
var arr = [
{
current_capacity: 6000,
picker_id: "icQrHPuE2fMZslceSG6liwuRar92",
process_time_in_minutes: 10,
products: [
{
product_id: 1,
picked_qty: 2
},
{
product_id: 2,
picked_qty: 3
}
]
},
{
current_capacity: 2500,
picker_id: "icQrHPuE2fMZslceSG6liwuRar92",
process_time_in_minutes: 20,
products: [
{
product_id: 1,
picked_qty: 10
}
]
},
{
current_capacity: 36000,
picker_id: "WIRzfIZALeftRk3DRGvh4nBdxQV2",
process_time_in_minutes: 15,
products: [
{
product_id: 1,
picked_qty: 2
},
{
product_id: 2,
picked_qty: 3
}
]
}
];
Here is my code:
var res = arr.reduce((acc, obj) => {
var existObj = acc.find((item) => item.picker_id === obj.picker_id);
if (existObj) {
let total_picked = obj.products.reduce((acc2, curr) => acc2 + curr);
// console.log("total_picked", total_picked);
existObj.current_capacity =
existObj.current_capacity + obj.current_capacity;
existObj.process_time_in_minutes =
existObj.process_time_in_minutes + obj.process_time_in_minutes;
existObj.total = existObj.total ? existObj.total : 0 + total_picked;
return acc;
}
acc.push(obj);
return acc;
}, []);
const formatted = res.map((el) => {
return {
picker_id: el.picker_id,
total_volume: el.current_capacity,
total_time: el.process_time_in_minutes,
total_products: el.total
};
});
The result is as below:
[
{
picker_id: "icQrHPuE2fMZslceSG6liwuRar92"
total_volume: 8500
total_time: 30
total_products: "0[object Object]"
},
{
picker_id: "WIRzfIZALeftRk3DRGvh4nBdxQV2"
total_volume: 36000
total_time: 15
total_products: undefined
}
]
Expected like below:
[
{
picker_id: "icQrHPuE2fMZslceSG6liwuRar92"
total_volume: 8500
total_time: 30
total_products: 15
},
{
picker_id: "WIRzfIZALeftRk3DRGvh4nBdxQV2"
total_volume: 36000
total_time: 15
total_products: 5
}
]
Using a little parameter destructuring, I think you can do a little further clean-up after you fix the problems others have described. My version might look like this:
const extract = (xs) => Object .values (xs .reduce (
(acc, {current_capacity, picker_id, process_time_in_minutes, products}) => {
const curr = acc [picker_id] || (acc [picker_id] = {
picker_id, total_volume: 0, total_time: 0, total_products: 0
})
curr .total_volume += current_capacity
curr .total_time += process_time_in_minutes
curr .total_products += products .reduce ((t, p) => t + p .picked_qty, 0)
return acc
},
{}
))
const arr = [{current_capacity: 6e3, picker_id: "icQrHPuE2fMZslceSG6liwuRar92", process_time_in_minutes: 10, products: [{product_id: 1, picked_qty: 2}, {product_id: 2, picked_qty: 3}]}, {current_capacity: 2500, picker_id: "icQrHPuE2fMZslceSG6liwuRar92", process_time_in_minutes: 20, products: [{product_id: 1, picked_qty: 10}]}, {current_capacity: 36e3, picker_id: "WIRzfIZALeftRk3DRGvh4nBdxQV2", process_time_in_minutes: 15, products: [{product_id: 1, picked_qty: 2}, {product_id: 2, picked_qty: 3}]}]
console .log (extract (arr))
.as-console-wrapper {max-height: 100% !important; top: 0}
You can also achieve your output by this
function getProductQty(arr){
let total = 0;
arr.forEach(prd => {
total += prd.picked_qty
})
return total;
}
const result = arr.reduce((acc,product) => {
if(!acc.hasOwnProperty(product.picker_id)){
acc[product.picker_id] = {
picker_id: product.picker_id,
total_volume: product.current_capacity,
total_time: product.process_time_in_minutes
}
acc[product.picker_id].total_products = getProductQty(product.products);
}else{
acc[product.picker_id].total_volume = acc[product.picker_id].total_volume + product.current_capacity
acc[product.picker_id].total_time = acc[product.picker_id].total_time + product.process_time_in_minutes
acc[product.picker_id].total_products = acc[product.picker_id].total_products + getProductQty(product.products);
}
return acc
},{})
console.log(Object.values(result),'result');
Issue with your implementation was if the existObj doesn't exit in your acc, you were directly pushing the obj instead you need to process the total first from the inner array of products.
I have updated your code to look cleaner and maintainable.
Approach:
build a dict for each picker_id which hold the computed data
convert dict to list
var result = arr.reduce((acc, obj) => {
if (!acc[obj.picker_id]) {
acc[obj.picker_id] = {
total_volume: 0,
total_time: 0,
total_products: 0
};
}
const selectedPicker = acc[obj.picker_id];
const total_picked = obj.products.reduce((acc2, item) => acc2 + item.picked_qty, 0);
selectedPicker.total_volume = selectedPicker.total_volume + obj.current_capacity;
selectedPicker.total_time =
selectedPicker.total_time + obj.process_time_in_minutes;
selectedPicker.total_products = selectedPicker.total_products + total_picked;
return acc;
}, {});
const formatted = Object.keys(result).reduce((acc, picker_id) => {
acc.push({
picker_id,
...result[picker_id]
})
return acc;
}, [])
console.log("formmated", formatted);
If you have an array of objects like so:
What's the best way to add all numerical values in each object so each one looks something like this:
{category: "A", total: 44}
So in the 0th item in the original array, 0+23+21 is 24, and is now represented by the new 'total' key.
Bearing in mind that the 'keys' with numerical values in the original array e.g. 'col2' are randomly generated (so another array like the original can have keys like 'somethingelse'.
I've attempted it with the following, but I believe it's not written correctly:
newArrayOfObjects.forEach(element => {
Object.values(element).reduce((a, b) => a + b);
});
It may be good to know but the 'key' category always exists in each object and is fixed. All other key values are numerical and there'll always be more than one.
Please check this.
const array = [
{
category: 'A',
col1: 1,
col2: 2,
col3: 3,
},
{
category: 'B',
col1: 2,
col2: 3,
col3: 4,
}
]
const result = array.map(obj => {
const total = Object.values(obj).reduce((acc, value) => {
if (typeof value === 'number') {
return acc + value;
}
return acc;
}, 0)
return {
category: obj.category,
total
}
})
console.log(result)
You could use Array.map() along with Array.reduce() to sum the numeric values in the array.
We'd create a toNumber() function to get the numeric value of any property. If this is not a number, it will return 0 (keeping the total unchanged).
let arr = [
{ a: 0, category: "a", col2: 23, col3: 21 },
{ b: 0, category: "b", x: 100, y: 10, z: 1 },
{ j: 0, category: "x", foo: 25, bar: 50, meta: 'content' },
]
function toNumber(n) {
return isNaN(n) ? 0: n;
}
function sumTotals(a) {
return a.map(({ category, ...obj}) => {
const total = Object.values(obj).reduce((total, value) => {
return total + toNumber(value);
}, 0);
return { category, total };
})
}
console.log('Totals:', sumTotals(arr))
.as-console-wrapper { max-height: 100% !important; }
arr = [{x:1}, {x:3}]
arr.reduce((accumulator, current) => accumulator + current.x, 0);
var data = [
{ "category": "A", "col0": 5, "col1": 8, "some": "thing"},
{ "category": "B", "col1": 3, "col2": 5}
];
var res = data.map((it) => {
const { category, ...rest } = it;
return {
...it,
total: Object.values(rest).reduce(
(prev, curr) =>
typeof curr === "number" ? prev + curr : prev, // add if the current value is numeric
0
)
}
});
console.log(res);
/**
[
{"category":"A","col0":5,"col1":8,"some":"tst","total":13},
{"category":"B","col1":3,"col2":5,"total":8}
]
**/
I think you are on the right way, you just need to do a bit more destructuring and type checking:
const aggregated = newArrayOfObjects.map((obj) =>
Object.entries(obj).reduce(
(newObj, [key, value]) => ({
...newObj,
...(typeof value === "number"
? { total: newObj.total + value }
: { [key]: value }),
}),
{ total: 0 }
)
);
First, you map all objects to their representations as key-value-pairs. Then you iterate over these key-value pairs and keep all non-numerical values and their respective keys, while dropping key-value-pairs with a numerical value and replacing them by a property in which you aggregate the total value.
I am having array of objects where i want to reduce the array and get the count of two properties. When my array length is more than one it works but when its one or less it doesn't
const data = [{id: 1, count:3 , alt_count: 2}, {id: 2, count:4 , alt_count: 5}];
const countObj = data.reduce((acc, curr) => {
return {
count_total: acc.count + curr.count,
alt_count_total: acc.alt_count + curr.alt_count,
};
});
console.log(countObj) // giving me both count_total and alt_count_total
But here its not giving me, instead it gives me the same object
const data = [{id: 1, count:3 , alt_count: 2}];
const countObj = data.reduce((acc, curr) => {
return {
count_total: acc.count + curr.count,
alt_count_total: acc.alt_count + curr.alt_count,
};
});
console.log(countObj) // giving me entire first object
Any help is appreciated
You need to provide the initial value to reduce.
const data = [{
id: 1,
count: 3,
alt_count: 2
}];
const countObj = data.reduce((acc, curr) => {
return {
count_total: acc.count + curr.count,
alt_count_total: acc.alt_count + curr.alt_count,
};
}, {
count: 0,
alt_count: 0
});
console.log(countObj);
How to make function take multiple variables from an array passed in as parameter?
Edited
For example:
Achieve this:
const inputObj = [
['Anna', 10, 'Monday'],
['Anna', 15, 'Wednesday'],
['Beatrice', 8, 'Monday'],
['Beatrice', 11, 'Wednesday'],
['Anna', 4, 'Wednesday'],
['Beatrice', 5, 'Monday'],
['Beatrice', 16, 'Monday']
]
// expected output:
const outputObj = [
[ 'Anna', 10, 'Monday' ],
[ 'Anna', 19, 'Wednesday' ],
[ 'Beatrice', 29, 'Monday' ],
[ 'Beatrice', 11, 'Wednesday' ]
]
const arr = [0, 2]
const someFunction = (obj, v, a) => {
const result = obj.reduce((acc, cur) => {
const key = `${cur[a[0]]}|${cur[a[1]]}`
if(!acc[key]) acc[key] = cur
else acc[key][1] += cur[v]
return acc
}, {})
return Object.values(result)
}
console.log(someFunction(inputObj, 1, arr))
with this:
const arr = [0, 2, 3, ...] // basically the array could contain any number of items.
const someFunction = (obj, v, objParams) => {
const result = obj.reduce((acc, cur) => {
const key = ???
...
}, {})
}
So that the function can be reused and it accepts custom-sized arrays, check if the column numbers in the array are the same, then adds the sum of the column that is passed in as v?
How to declare the variables from the objParams to achieve the same result as the code above does?
Also how to add v in the middle of cur?
Assuming objParams is an array of unknown size (strings in this example):
const objParams = ["c1", "c2", "c3"];
const key = objParams.join(']}|${cur[');
const built = '${cur[' + key + ']';
Built is:
${cur[c1]}|${cur[c2]}|${cur[c3]
With ES6 you can use the spread operator in the argument definition.
More reading about spread operator on MDN
function sum(...args) {
return args.reduce((result, value) => result + value, 0)
}
const numbers = [1, 2, 3];
console.log('sum', sum(2, 2));
console.log('sum', sum(...numbers));
console.log('sum', sum(1, 2, 1, ...numbers));
// get single args before accumulating the rest
function sum2(foo, bar, ...args) {
return args.reduce((result, value) => result + value, 0)
}
console.log('sum2', sum2(2, 2));
console.log('sum2', sum2(...numbers));
console.log('sum2', sum2(1, 2, 1, ...numbers));
I am kinda new to javascript and today I encountered problem. Thing is, I have an array of amount of people visited every day by each hour (as you can see bellow). And I would like to find out most popular hour of day. My plan was to create a map where key is index of hour (0, 1, 2, 3, 4, 5...) and value is sum of all people who visited across all days on that hour. Problem is I'm not able to do that with my JS knowledge. Can someone give me a direction how to approach this problem? Thank you very much.
[
{date: "25.05.2018",
value: {
1: 209
2: 123
3: 890
.
.
24: 789
}
},
{date: "26.05.2018",
value: {
1: 280
2: 398
3: 450
.
.
24: 76
}
}
]
My JAVA like solution:
const { data: { data: [{ values }] } } = insightsData;
const timesMap = new Map();
values.forEach(item => {
Object.entries(item.value).forEach(([key, value]) => {
const timeValue = timesMap.get(key);
if (timeValue) {
timesMap.set(key, timeValue + value);
} else {
timesMap.set(key, value);
}
});
});
You could use Array#reduce with Array#map if you have arrays with the same length.
reduce takes an array as accumulator r and uses a logical OR || with a zero as value if an item does not exist in the accumulator.
var counts = [{ date: "26.05.2018", value: [125, 100, 200] }, { date: "27.05.2018", value: [5, 6, 7] }, { date: "28.05.2018", value: [3, 4, 5] }],
result = counts.reduce(
(r, { value }) => value.map((v, i) => (r[i] || 0) + v),
[]
);
console.log(result);
With objects as value properties.
var counts = [{ date: "26.05.2018", value: { 0: 125, 1: 100, 2: 200 } }, { date: "27.05.2018", value: { 0: 5, 1: 6, 2: 7 } }, { date: "28.05.2018", value: { 0: 3, 1: 4, 2: 5 } }],
result = counts.reduce(
(r, { value }) => Object
.entries(value)
.reduce((s, [k, v]) => {
s[k] = (s[k] || 0) + v;
return s;
}, r),
{}
);
console.log(result);
You can do iteratre over the values and add them to sum, like this
const data = {date: "26.05.2018",
value: [
125,
100,
200,
]
}
let sum = 0;
Object.values(data.value).forEach(elem => sum = sum + elem)
console.log(sum)
https://jsfiddle.net/5wwzn4yt/