Sum object values with the same key - javascript

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/

Related

Transform nested object of objects of arrays in JS

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);

How to add all numerical key-values in array of objects?

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.

Value change in 1 object changes in all objects in array

I have an array of objects. Each object has a key quantity and value. I want to duplicate each object in the array based on its quantity. Next, I want to manipulate only one of the duplicated object in the array. But on manipulating value of 1 object, value of all duplicated objects change. Here is my code:
let arr = [
{ id: 1, quantity: 3, value: 10 },
{ id: 2, quantity: 1, value: 5 },
{ id: 2, quantity: 5, value: 5 },
];
const newArr = [];
for (const a of arr) {
if (a.quantity > 1) {
let quantity = a.quantity;
a.quantity = 1;
while (quantity--) {
newArr.push(a);
}
}
}
arr = newArr;
arr[0].value = 1;
When I changed the value of arr[0] to 1, value field of arr[1] and arr[2] also changed to 1.
I have tried copying the object using spread operator and JSON.parse(JSON.parse()), but none has worked.
Because newArr.push(a) .a push to newArr ref to element of arr
You can edit same as :
let arr = [
{ id: 1, quantity: 3, value: 10 },
{ id: 2, quantity: 1, value: 5 },
{ id: 2, quantity: 5, value: 5 },
]
const newArr = []
for (const a of arr) {
if (a.quantity > 1) {
let quantity = a.quantity;
a.quantity = 1;
while (quantity--) {
newArr.push({...a})
}
}
}
arr = [...newArr]
arr[0].value = 1
console.log(arr)
// example for Memory Management
let a = { id: 1, quantity: 3, value: 10 }
let b = { id: 1, quantity: 3, value: 10 }
let c = arr[0]
let d = {...arr[0]}
console.log(a === arr[0]) // false : different allocates memory for contain value
console.log(a === b) // false : different allocates memory for contain value
console.log(c === arr[0]) // true : refer to a memory
console.log(d === arr[0]) // false : different allocates memory for contain value

Is there a better way of writing a function like this

The function below takes two arguments and returns an array of objects. Each object should be returned in descending order in reference to the availableBagSizes array.
I gave two examples, I want to know if there is a better solution to achieving the same output and why my solution is bad.
I need help with the third example it's not returning as expected.
function getBagCounts(clientOrders, availableBagSizes) {
// TODO: remove this hard-coded solution for test scenario
// clientOrders === [9]
// sorting the availablebag size in descending order
const newAvailableBag = availableBagSizes.sort((a, b) => b - a);
const result = [];
let newRemainder;
for (let index = 0; index < clientOrders.length; index++) {
const clientOrder = clientOrders[index];
// set the newremainder variable to clientOrder for the first loop
newRemainder = index === 0 ? clientOrder : newRemainder;
for (let j = 0; j < availableBagSizes.length; j++) {
const bagSize = newAvailableBag[j];
const count_result = Math.floor(newRemainder / bagSize);
newRemainder = newRemainder % bagSize;
const obj = {};
if (newRemainder > bagSize) {
result.push({ size: bagSize, count: 0 });
continue;
}
// checking if it is the last item in the bagSizes
if (j + 1 === availableBagSizes.length) {
// setting the newreaminder to the next number of client order
newRemainder = clientOrders[index + 1];
}
result.push({ size: bagSize, count: count_result });
}
}
return result;
}
// first example
const clientOrders = [9];
const availableBagSizes = [1, 2, 4];
const expectedoutput = [
{ size: 4, count: 2 },
{ size: 2, count: 0 },
{ size: 1, count: 1 }
];
// second example
const clientOrders = [5, 12, 12];
const availableBagSizes = [1, 2, 4];
const expectedoutput = [
{ size: 4, count: 1 },
{ size: 2, count: 0 },
{ size: 1, count: 1 },
{ size: 4, count: 3 },
{ size: 2, count: 0 },
{ size: 1, count: 0 },
{ size: 4, count: 2 },
{ size: 2, count: 1 },
{ size: 1, count: 0 }
];
// third example
const clientOrders = [4.5];
const availableBagSizes = [1, 2, 4];
const expectedoutput = [
{ size: 4, count: 1 },
{ size: 2, count: 0 },
{ size: 1, count: 0.5 }
];
It looks good to me.
You should think about performance and check of parameters if you want a good code.
if (!Array.isArray(clientOrders) || !Array.isArray(availableBagSizes)) {
return null;
}
Also you should try to use forEarch loop which is faster in performance
Making a push is slow, better make a .map((element,index)=>{return null})
It really depends how you manage your data, but I would say first loop is a forEach, and second loop you make a map. Because whatever the case in your second loop, all the time you make a push there would be no null or undefined return in your mapped array.

Find the minimum and maximum values in the nested object

I have a deeply nested javascript object with an unlimited amout of children. Every child has a value.
var object = {
value: 1,
children: {
value: 10,
children:{
value: 2,
children: {...}
}
}
}
All attempts to make a recursive function did not succeed, it turned out to go down only to a lower level.
After flattening your linked list into an array, you can use Array.prototype.reduce() with an accumulator that is a tuple of min and max, starting with initial values of Infinity and -Infinity respectively to match the implementations of Math.min() and Math.max():
const object = {
value: 1,
children: {
value: 10,
children: {
value: 2,
children: {
value: 5,
children: null
}
}
}
}
const flat = o => o == null || o.value == null ? [] : [o.value, ...flat(o.children)]
const [min, max] = flat(object).reduce(
([min, max], value) => [Math.min(min, value), Math.max(max, value)],
[Infinity, -Infinity]
)
console.log(min, max)
Since children is an object with only one value (vs an array with potentially many), this is a pretty simple recursive function. The base case is when there are no children in which case both min and max are just the value. Otherwise recurse on the children to find the min and max:
var object = {
value: -10,
children: {
value: 4,
children:{
value: 200,
children: {
value: -100,
children: null
}
}
}
}
function getMinMax(obj) {
if (!obj.children || obj.children.value == undefined)
return {min: obj.value, max: obj.value}
else {
let m = getMinMax(obj.children)
return {min: Math.min(obj.value, m.min), max: Math.max(obj.value, m.max)}
}
}
console.log(getMinMax(object))
Short and simple, for min change Math.max to Math.min
var test = {
value: 1,
children: {
value: 10,
children:{
value: 2,
children: {}
}
}
}
function findMaxValue(obj) {
if (Object.keys(obj.children).length === 0) {
return obj.value;
}
return Math.max(obj.value, findMaxValue(obj.children))
}
console.log(findMaxValue(test))

Categories

Resources