Group and sum of an array of objects based on number key - javascript

I am trying to create a statistical pie chart. As a http response i am getting a list from server using which i need to draw a pie chart.
For example: Data received:
[{1: 9, 2: 7}, {3:8, 2: 1}, {1:8, 5:9}, {2:3, 3:1}]
This is the desired output:
[{x: 1, y: 17}, {x: 2, y:10}, {x: 3, y: 9}, {x: 5, y: 9}]
Please note: x is the key and y is sum of similar key values
I have tried data.forEach((item, index) => {}). After writing this, I am actually getting no lead about how I can combine Object.keys(item), Object.values(item) and Object.values(item).reduce((a,b)=> return a+b;)
This may sound silly question, but any help would be appreciated. :)

You could reduce the array. Create an accumulator object with each number as key and and object with x and y keys as it's value. Loop through each object and update the y value based on the number. Then use Object.values() on the object returned to get the values of the accumulator as an array
const input = [{1: 9, 2: 7}, {3:8, 2: 1}, {1:8, 5:9}, {2:3, 3:1}]
const grouped = input.reduce((acc, obj) => {
for (const x in obj) {
acc[x] = acc[x] || { x , y: 0 }
acc[x].y += obj[x]
}
return acc;
}, {})
console.log(Object.values(grouped))

You could look for same key and update or insert a new object.
This approach does not change the order of the keys.
var data = [{ 1: 9, 2: 7 }, { 3: 8, 2: 1 }, { 1: 8, 5: 9 }, { 2: 3, 3: 1 }] ,
result = data.reduce((r, o) => {
Object.entries(o).forEach(([x, y]) => {
var temp = r.find(o => o.x === x);
if (temp) temp.y += y;
else r.push({ x, y });
});
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Javascript: calculate the total sum of all the object values in an array

I have an array of objects as the following;
[{"A":"34"},{"B":"13"},{"C":"35"},{"D":"74"}]
If the key is A, it's value has to be multiply by 30,B by 10,C by 5,D by 2. I would like to calculate the total sum after the multiplication;
34*30 + 13*10 + 35*5 + 74*2
Is there a way to achieve this other than an if/else statement? Thanks!
Reduce the array, and get the key / value pair by destructuring the array produce by calling Object.entries() on the each item. Get the value of the key, multiply by current value, and add to the accumulator.
const multi = { A: 30, B: 10, C: 5, D: 2 }
const fn = arr =>
arr.reduce((acc, item) => {
const [[k, v]] = Object.entries(item)
return acc + multi[k] * v
}, 0)
const arr = [{"A":"34"},{"B":"13"},{"C":"35"},{"D":"74"}]
const result = fn(arr)
console.log(result)
let input = [{"A": "34"}, {"B": "13"}, {"C": "35"}, {"D": "74"}];
let multiply = {A: 30, B: 10, C: 5, D: 2};
let result = input.reduce((sum, v) =>
sum + Object.values(v)[0] * multiply[Object.keys(v)[0]], 0);
console.log(result);
You can easily achieve this using Object.entries
const arr = [{ A: "34" }, { B: "13" }, { C: "35" }, { D: "74" }];
const multiplier = {
A: 30,
B: 10,
C: 5,
D: 2,
};
const result = arr.reduce((acc, curr) => {
const [[key, value]] = Object.entries(curr);
return acc + multiplier[key] * parseInt(value);
}, 0);
console.log(result);
You can create an dictionary to get the number with which the value should be multiplied as shown :
{"A":30, "B":13, "C":35, "D":74}
Now you can loop through your array of objects, and fetch the value from the dictionary using the key of the object:
const myArray = [{"A":"34"},{"B":"13"},{"C":"35"},{"D":"74"}]
const Nums = {"A":30, "B":10, "C":5, "D":2};
let Result = 0;
myArray.forEach((item)=>{
const key = Object.keys(item)[0];
var temp= parseInt(item[key]);
Result += temp*Nums[key];
})
console.log(Result);
Not sure how you map your values so that "A" === 30. But assuming you have a map:
const map = {
"A": 30,
"B": 10,
"C": 5,
"D": 2
}
const array = [{"A":"34"},{"B":"13"},{"C":"35"},{"D":"74"}];
Then in one line:
const outcome = array.map(element => map[Object.keys(element)[0]] * Object.values(element)[0]).reduce((acc, init) => acc + init, 0)

Targeting two instances of key-value pair in specific indices of nested array

Say I have a nested array something like
var allPairs = [
[
{ x: 1, y: 0 },
{ x: 1, y: 1 },
],
[
{ x: 1, y: 1 },
{ x: 1, y: 2 },
],
[
{ x: 1, y: 2 },
{ x: 2, y: 2 },
],
]
and I have an object
var currentPair = {x: 1, y: 1};
I've been struggling with trying to find a way to check if, in allPairs, currentPair equals the second index [1] of one nested array, while also equaling the first index [0] of another nested array.
I've been playing around with some:
if (arrOfPointsUsed.some(e => e[1] === firstPoint)) {
pointsFound++;
}
if (arrOfPointsUsed.some(e => e[0] === firstPoint)) {
pointsFound++;
}
if (pointsFound > 1) {
return false;
}
I've tried nested for loops also.
What is the best approach?
I think you're on the right track with some, but I think you'll need two calls to it:
const foundBoth =
// Found 0?
allPairs.some(([{x, y}]) => x === currentPair.x && y == currentPair.y)
&&
// Found 1?
allPairs.some(([, {x, y}]) => x === currentPair.x && y == currentPair.y)
;
Live Example:
var allPairs = [ [{x: 1, y: 0}, {x: 1, y: 1}], [{x: 1, y: 1}, {x: 1, y: 2}], [{x: 1, y: 2}, {x: 2, y: 2} ] ];
var currentPair = {x: 1, y: 1};
const foundBoth =
// Found 0?
allPairs.some(([{x, y}]) => x === currentPair.x && y == currentPair.y)
&&
// Found 1?
allPairs.some(([, {x, y}]) => x === currentPair.x && y == currentPair.y)
;
console.log(foundBoth);
Or to do it in one pass, you could use a loop:
let foundBoth = false;
let found0 = false;
let found1 = false;
for (const [e0, e1] of allPairs) {
found0 = found0 || e0.x === currentPair.x && e0.y === currentPair.y;
found1 = found1 || e1.x === currentPair.x && e1.y === currentPair.y;
foundBoth = found0 && found1;
if (foundBoth) {
break;
}
}
Live Example:
var allPairs = [ [{x: 1, y: 0}, {x: 1, y: 1}], [{x: 1, y: 1}, {x: 1, y: 2}], [{x: 1, y: 2}, {x: 2, y: 2} ] ];
var currentPair = {x: 1, y: 1};
let foundBoth = false;
let found0 = false;
let found1 = false;
for (const [e0, e1] of allPairs) {
found0 = found0 || e0.x === currentPair.x && e0.y === currentPair.y;
found1 = found1 || e1.x === currentPair.x && e1.y === currentPair.y;
foundBoth = found0 && found1;
if (foundBoth) {
break;
}
}
console.log(foundBoth);
...but allPairs would have to be massive for making one pass vs. two to make a difference.
Some notes on the above:
I've assumed that the currentPair object and the matching objects in allPairs are merely equivalent, not the same object. That's why I'm checking x and y, not === on the object themselves. (Because two separate objects are never === one another.)
I'm using destructuring in various places, both iterable destructuring to pick out the 0th and 1st entries from each of the subarrays and object destructuring to pick out the x and y properties of those objects. In the first example (using some), [{x, y}] in the first some call's callback function's parameter list picks the x and y properties out of the first entry in the subarray, and [,{x, y}] does the same for the second (skipping the first entry with the , at the beginning).
If it were even one tiny bit more complex to compare the objects than just the x and y checks above, I'd factor that comparison operation out into its own function and reuse it.
In a comment you've asked:
Thanks for your thorough answer. Just trying to understand your syntax in the first example: how would I, for instance, find if the first index is equivalent to currentPair in more than one array within allPairs
For that I'd probably use find or findIndex twice: The first time to find the first index, the second time to find another one that isn't the first one. For instance:
const firstIndex = allpairs.findIndex(([{x, y}]) => x === currentPair.x && y == currentPair.y);
const secondIndex = firstIndex === -1 ? -1 : allpairs.findIndex(([{x, y}], index) => index > firstIndex && x === currentPair.x && y == currentPair.y);
if (secondIndex !== -1) {
// Found in the first entry of two different subarrays
}
You need to check the values instead of the object reference.
For the check, you need the entries of the target object and iterate this as well.
const
allPairs = [[{ x: 1, y: 0 }, { x: 1, y: 1 }], [{ x: 1, y: 1 }, { x: 1, y: 2 }], [{ x: 1, y: 2 }, { x: 2, y: 2 }]],
currentPair = { x: 1, y: 1 },
entries = Object.entries(currentPair),
result = allPairs.some(array =>
array.some(object =>
entries.every(([key, value]) => object[key] === value)
)
);
console.log(result);

Is there any direct method which returns the sum of lengths of arrays in the array of objects?

I have an array of objects and I want to find the sum of length of arrays of a certain property(key).
I have this array of objects like
var myArray =
[{
"a" : 1,
"b" : another Array
},
{
"c" : 2,
"b" : another Array
}
.....
]
Is there any way to simplify this below process?
var lengthOfEachObject = myArray.map(function(Obj){
if(Obj.b){
return Obj.b.length;
}
else{
return 0;
}
});
lengthofEachObject.reduce(function(x,y){
return x+y;
})
Answers can also include use of external libraries.
You can use .reduce without .map, this way you can get the total sum by only iterating once over your array. Furthermore, you can use destructing assignment instead of your if statements to set a default value for your b.length if it doesn't exist.
See working example below:
const arr = [{a: 1, b: [1, 2, 3, 4, 5] }, {c: 2, b: [1, 2, 3]}, {e: 3}],
total = arr.reduce((acc, {b={length:0}}) => acc + b.length, 0);
console.log(total);
You can use lodash's _.sumBy():
var myArray = [{"a":1,"b":[1,2,3]},{"c":2,"b":[4,5,6,7,8]},{"c":2}]
var result = _.sumBy(myArray, 'b.length')
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could pull out the length with a closure over the wanted key and map this value.
const
length = k => ({ [k]: { length = 0 } = {} }) => length,
add = (a, b) => a + b,
array = [{ a: 1, b: [1, 2, 3, 4] }, { c: 2, b: [1, 2, 3] }];
console.log(array.map(length('b')).reduce(add));
console.log(array.map(length('a')).reduce(add));

Flatten different properties into single array

I have a javascript object literal as follows.
data: {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
}
the output should be like -
output: [{zone: "Zone1", x: 1, y: "1302.5", z: 0}, {zone: "Zone2", x: 5, y: 0, z: 2}]
I can do it in trivial way like first add prop1ByZones to output and then loop through prop2ByZones and prop3ByZones and check for existing zone. if the zone is there then update it else add it.
I just wanted to check if there is any elegant way of doing it. Please let me know.
One possible approach:
var data = { prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}], prop2ByZones:[{zone: "Zone1", y: "1302.5"}], prop3ByZones: [{zone: "Zone2", z: 2}] }
var arr = [...data.prop1ByZones, ...data.prop2ByZones, ...data.prop3ByZones]
var resp = arr.reduce((acc, { zone, x, y, z }) => {
var prev = acc.find(x => zone == x.zone);
if(prev) {
prev.x = x ? x : prev.x,
prev.y = y ? y : prev.y,
prev.z = z ? z : prev.z
return acc;
}
return acc.concat({zone: zone, x: x ? x : 0, y: y ? y : 0, z: z ? z : 0});
}, []);
console.log(resp)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a way to do this (using lodash for iterating), you could optimize the conditionals a bit to be ternaries etc, but this would give you your output:
const data = {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
};
let list = {};
_.each(data, (d, i) => {
_.each(d, (e) => {
const zone = e.zone;
if (!list[zone]) {
list[zone] = zone;
list[zone] = {};
}
if (!list[zone].x) {
list[zone].x = e.x || 0;
}
if (!list[zone].y) {
list[zone].y = e.y || 0;
}
if (!list[zone].z) {
list[zone].z = e.z || 0;
}
});
});
// put everything in an array
let result = [];
_.each(list, (obj, k) => {
result.push({
zone: k,
x: obj.x,
y: obj.y,
z: obj.z
});
});
console.log(result);
You can get the Object.values(), flatten to a single array by spreading into Array.concat(), and then reduce the array to a Map, and spread the Map.values() iterator back to array:
const data = {
prop1ByZones:[{zone: "Zone1", x: 1}, {zone: "Zone2", x: 5}],
prop2ByZones:[{zone: "Zone1", y: "1302.5"}],
prop3ByZones:[{zone: "Zone2", z: 2}]
}
const result = [... // spread the map values iterator (see end) back to an array
[].concat(...Object.values(data)) // get the object's values and flatten to a single array
.reduce( // reduce the array to a Map
// add the zone key to the map, and include the previous and current item
(r, o) => r.set(o.zone, { ...(r.get(o.zone) || {}), ...o }),
new Map()
).values()] // get the map values iterator
console.log(result)

Javascript - Compare array of objects and sum coincident values

I have this array of objects:
arr=[
{a: 1,b: 0,x: 100},
{a: 0,b: 0,x: 100},
{a: 1,b: 1,x: 100},
{a: 1,b: 0,x: 200},
{a: 1,b: 1,x: 200},
....
]
Now, what I need to do is to compare "x" values and if they coincide, tranfer summed "a" and "b" values in another array. For example:
arr2=[{a=2,b=1,x=100},{a=2,b=1,x=200}....]
Second thing to do, is to count how many objects are been joined with the same "x" value. For example in the first "arr2" object are joined 3 "arr" objects and in the second "arr2" object are joined 2 "arr" objects and so on.
This question doesn't seem to make sense. First of all, the word "coincident" doesn't have a technical definition that I'm aware of. Can you be more clear about what you mean?
Secondly its not clear what your expected results are. Perhaps what I would do if I was you would be to start with some simple example inputs and then come up with what you would expect the corresponding output to be, then use a unit testing tool to develop the code to do what you want.
For example, I have to guess what you are wanting but it might look like this (in javascript) using the libraries chai and mocha:
import { expect } from 'chai'
function doWork(input) {
// code goes here
}
const tests = [
{
name: 'Same x values coalesce',
data: [
{a=1,b=0,x=100},
{a=0,b=0,x=100}
],
expected: {
100: [1, 0]
}
}
]
describe('work', () => {
tests.forEach(test => {
it(t.name, () => {
const result = doWork(test.data)
expect(result).to.deep.equal(test.expected)
})
})
})
This technique may help you come to to an answer on your own.
Try to be a little more precise with your terms and give an example of what you are expecting to get as output.
Array reduce and map are fun methods. Object.keys() will give you an array of keys and that lets you do more array reducing and mapping. Fun times.
let arr = [
{ a: 1, b: 0, x: 100 },
{ a: 0, b: 0, x: 100 },
{ a: 1, b: 1, x: 100 },
{ a: 1, b: 0, x: 200 },
{ a: 1, b: 1, x: 200 }
];
let lists = arr.reduce((prev, curr) => {
let list = prev[curr.x] || [];
list.push(curr);
prev[curr.x] = list;
return prev;
}, {});
console.log(lists);
let flat = Object.keys(lists).reduce((prev, curr) => {
prev.push(
lists[curr].reduce(
(prev, curr) => {
prev.a += curr.a;
prev.b += curr.b;
prev.x = curr.x;
return prev;
},
{ a: 0, b: 0 }
)
);
return prev;
}, []);
console.log(flat);
Object.keys(lists).forEach(el => console.log(el + ': ' + lists[el].length));

Categories