Javascript: Merge Array of Objects, summing values with same key - javascript

I already searched for my issue, however, i did not find something, that matches my needs. I want to merge (sum) multiple series of data into a single array. Any dataset that matches the key, shall be summed in the resulting array.
Please see sample data and expected result:
var power1 = [
{
"time": 10,
"power": 100
},
{
"time": 20,
"type": 200
},
{
"time": 30,
"type": 300
}
]
var power2 = [
{
"time": 20,
"type": 200
},
{
"time": 30,
"type": 300
},
{
"time": 40,
"type": 400
}
]
var result = [
{
"time": 10,
"type": 100
},
{
"time": 20,
"type": 400
},
{
"time": 30,
"type": 600
},
{
"time": 40,
"type": 400
}
]
Since this should happen with thousands of items, it should be reasonable fast. Could a Map as intermediate help here?
Thanks in advance

You can concat the two arrays and then perform a reduce operation over it with an object to store the values for each key.
var power1=[{time:10,type:100},{time:20,type:200},{time:30,type:300}],power2=[{time:20,type:200},{time:30,type:300},{time:40,type:400}];
const res = Object.values(
power1.concat(power2).reduce((acc, {type, time})=>{
(acc[time] ??= {time, type: 0}).type += type;
return acc;
}, {})
);
console.log(res);

Related

How to bind list of objects to handsontable?

I have a json object,which contains following data.
var myData = [{
"sNo": "1.0",
"boqName": "Installation of pole 11 KV",
"unit": "Nos",
"opportunityBoqOverHeadMasterList": [{
"opportunityBoqOverHeadMasterId": 14,
"value": 41.3
},
{
"opportunityBoqOverHeadMasterId": 16,
"value": 41.3
},
{
"opportunityBoqOverHeadMasterId": 24,
"value": 100
}
]
},
{
"sNo": "2.0",
"boqName": "Installation of pole 33 KV",
"unit": "Nos",
"opportunityBoqOverHeadMasterList": [{
"opportunityBoqOverHeadMasterId": 15,
"value": 52.92
},
{
"opportunityBoqOverHeadMasterId": 17,
"value": 52.92
},
{
"opportunityBoqOverHeadMasterId": 25,
"value": 0
}
]
},
];
}
i need to display the value of opportunityBoqOverHeadMasterList next to the unit cell.
Please help me to display data in handsontable. Please refer jsfiddle for working example https://jsfiddle.net/Umanath16/aty5wfg7/22/
You could try processing the data. Notice below that I changed your columns array, create a new myNewData variable and create nested loops to format the data.
var columns = [
{
"data": "sNo"
},
{
"data": "boqName"
},
{
"data": "unit"
},
,
{
"data": "Percentage1"
},
{
"data": "Percentage2"
},
{
"data": "Percentage3"
}
];
Change the data obj...
var myNewData = [];
for (var i = 0; i < myData.length; i++) {
myNewData.push({
sNo: myData[i].sNo,
boqName: myData[i].boqName,
unit: myData[i].unit
});
for (var j = 0; j < myData[i].opportunityBoqOverHeadMasterList.length; j++) {
myNewData[i]["Percentage"+(j+1)] = myData[i].opportunityBoqOverHeadMasterList[j].value;
}
}
hot = new Handsontable(example, {
data: myNewData,
rowHeaders: true,
columns: columns,
rowHeaderWidth: 100,
colWidths: 120,
colHeaders: colHeaders
});
}
This is just one possible solution. Please think about possible improvements. What if the data does not have the same fields? It would need to be dynamic regardless of the field names. Also how would you improve the loops, etc.

How can I add/remove elements based on total count of element value of another element value in Javascript

I am trying to add/remove elements from array based on total count of another value.
let data = [
{
"details": [
{
"Name": "DataSet1",
"Severity": "4",
"Cost": 20
},
{
"Name": "DataSet2",
"Severity": "4",
"Cost": 30
},
{
"Name": "DataSet3",
"Severity": "4",
"Cost": 25
}
],
"charge": 45
}
];
Here, I am trying to get an array such that total value of Cost is near to charge. Here, Sum of Cost is 75 which is greater than 45, so I tried like below:
Calculate difference between charge and total cost, If it's greater than charge, then remove elements from array, such that new array total cost always less or near to charge. So, my new array should be:
let newdata = [
{
"details": [
{
"Name": "DataSet1",
"Severity": "4",
"Cost": 20
},
{
"Name": "DataSet3",
"Severity": "4",
"Cost": 25
}
],
"charge": 50
}
];
Is there any better way to do this? Does Javascript have any built-in functionality that does this?
const data = {
"charge": 50,
"details": [{
"Name": "DataSet1",
"Severity": "4",
"Cost": 20
},
{
"Name": "DataSet2",
"Severity": "4",
"Cost": 30
}, {
"Name": "DataSet3",
"Severity": "4",
"Cost": 25
}
],
};
const getItemsAndDontGoAboveCharge =
(charge, list, start = 0, currentCost = 0, result = []) => {
if (start === list.length) return result
const {
Cost
} = list[start]
const nextTotal = currentCost + Cost
if (nextTotal > charge) return result
return getItemsAndDontGoAboveCharge(charge, list, start + 1, nextTotal, [...result, list[start]]);
}
console.log(getItemsAndDontGoAboveCharge(data.charge, data.details))

Accessing nested array properties

This may be extremely simple but I've not been able to figure out how to iterate over and access the properties in the following mix (I think) of arrays and nested objects:
myFilters = {
"color_Filter": [{
"name": "BLUE",
"count": 1,
"dataId": "BLUE"
},
{
"name": "Black",
"count": 5,
"dataId": "Black"
},
{
"name": "Blue",
"count": 14,
"dataId": "Blue"
}
],
"size_Filter": [{
"name": "10",
"count": 16,
"dataId": "10"
},
{
"name": "12",
"count": 16,
"dataId": "12"
}
]
}
What would the correct looping structure be here to pull out name, count etc from the above? The desired output is to output a string from the above with color_Filter=BLUE,Black,Blue/size_Filter=10,12
I've tried a few different approaches and none of them have been successful so far.
You could map the entries of the object and create a string for each key. Get the name from the value array using map. Then join the array of strings with a /
const myFilters = {color_Filter:[{name:"BLUE",count:1,dataId:"BLUE"},{name:"Black",count:5,dataId:"Black"},{name:"Blue",count:14,dataId:"Blue"}],size_Filter:[{name:"10",count:16,dataId:"10"},{name:"12",count:16,dataId:"12"}]};
const output = Object.entries(myFilters)
.map(([k,arr]) => `${k}=${arr.map(a => a.name)}`)
.join("/")
console.log(output)

sort object by key by using lodash but key has lost after sorted

I want to sort some Object look likes this
data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
}
I want to sort it by "name".
I tried to use Lodash for this problem.
_.sortBy(data, [function(o) { return o.name; }]);
but, it return me an array of objects without the keys
[
{
"name": "Emmy",
"age": 20
},
{
"name": "Nacky",
"age": 12
},
{
"name": "Nat",
"age": 20
}
]
I want it return me sorted object with key like the same
{
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
}
what should I do? thanks
Objects in JS can't be sorted, and the order of the properties is not reliable, ie it depends on browsers' implementations. That's why _.sortBy() is converting your object into a sorted array.
I can think of 2 options to work with that.
Add the key to the objects in the array
If you just need an ordered array with the keys in the objects, so you can render a list.
var data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
};
var result = _(data)
.map(function(v, k) { // insert the key into the object
return _.merge({}, v, { key: k });
})
.sortBy('name') // sort by name
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Create an order array
Create an array of ordered keys, and use them when you wish to render the objects in order.
var data = {
"imH3i4igFNxM3GL": {
"name": "Nacky",
"age": 12
},
"vuzPuZUmyT8Z5nE": {
"name": "Emmy",
"age": 20
},
"OkIPDY1nGjxlq3W": {
"name": "Nat",
"age": 20
}
};
var orderArray = _(data)
.keys() // create an array of keys
.sortBy(function(key) { // sort the array using the original names
return data[key].name;
}) // sort by name
.value();
console.log('The order array', orderArray);
console.log(orderArray.map(function(k) {
return data[k];
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
I use something like this.
let data = {
'g34ghgj3kj': {
YOUR_KEY: 'g34ghgj3kj',
'key1': false,
'key2': false,
},
'hh334h664': {
YOUR_KEY: 'hh334h664',
'key1': true,
'key2': false,
},
//{...}
};
_.orderBy(data, ['key1', 'key2'], ['desc', 'desc']).reduce((result, value) => {
result[value.YOUR_KEY] = value;
return result;
}, {});

Create key, value pair for JSON via nest in D3

I would like to create a key, value pair for a JSON using d3.nest().
The original JSON appears like this:
[
{
"Country": "Mexico",
"Climate": 100,
"Safety Index": 50,
"Life Expectancy": 80,
"Corruption": 30,
"Per Capita Income": 20
},
{
"Country": "UK",
"Climate": 70,
"Safety Index": 80,
"Life Expectancy": 70,
"Corruption": 70,
"Per Capita Income": 80
},
{
"Country": "US",
"Climate": 80,
"Safety Index": 70,
"Life Expectancy": 90,
"Corruption": 70,
"Per Capita Income": 80
}
]
I would like to transform it into this:
[
{"key": "Mexico", "value":
[
{ "key": "Climate", "value": 100 },
{ "key": "Safety Index", "value": 50 },
{ "key": "Life Expectancy", "value": 80 },
{ "key": "Corruption", "value": 30 },
{ "key": "Per Capita Income", "value": 20 }
]},
{"key": "UK", "value":
[
{ "key": "Climate", "value": 70 },
{ "key": "Safety Index", "value": 80 },
{ "key": "Life Expectancy", "value": 70 },
{ "key": "Corruption", "value": 70 },
{ "key": "Per Capita Income", "value": 80 }
]},
{"key": "US", "value":
[
{ "key": "Climate", "value": 80 },
{ "key": "Safety Index", "value": 70 },
{ "key": "Life Expectancy", "value": 90 },
{ "key": "Corruption", "value": 70 },
{ "key": "Per Capita Income", "value": 80 }
]}
]
My attempt:
var nest = d3.nest()
.key(function(d) { return d.Country; })
.entries(data);
My question is very basic as I'm new to D3. How should I go about changing the structure of the JSON in D3?
EDIT
This is to create a separate bar chart per country based on the different categories similar to this example ( http://bl.ocks.org/mbostock/5872848 ). If there's a better way I should be thinking about this, let me know.
If you want the exact output that you've provided, and insist on using nests for this, you can use:
var nest = d3.nest()
.key(function(d) { return d.Country; })
.rollup(function(d) {
var countryGroupedObject = d[0];
return d3.nest()
.key(function(innerKey) { return innerKey; })
.rollup(function(innerKey) { return countryGroupedObject[innerKey]; })
.entries(Object.keys(countryGroupedObject));
})
.entries(data);
A potential issue here is that you're assuming that each country will be unique (e.g. there won't be two "Mexico" entries). As such the inner nest only operates on d[0] (which will be the first item listed for a given country).
Edit: Actually, this won't be exactly what you asked for, the country key:value will exist in the inner grouping. If that's a problem, it can be ignored by placing the following before the inner nest:
delete countryGroupedObject.Country;
Edit 2: The above code uses nested nest functions simply because the question asked it that way. In real systems, the inner nest makes more sense to me to be pure Javascript, along the lines of:
function getGraphParams(countryObj){
return Object.keys(countryObj)
.filter(function(key){ return key !== "Country"; })
.map(function(key){ return { key: key, value: countryObj[key] }; })
}
Here's a solution without D3:
var a = ...; // Original Array of JSON
var b = [];
a.forEach(function(item) {
var r = {};
r.key = item.Country;
r.value = [];
for (var i in item) {
if (i === "Country") { continue; }
r.value.push({"key": i, "value": item[i]});
}
b.push(r);
});
Not sure why you're only using "key" as your key and "value" for you value keys though, that seems to go against the point using implicit keys and values in JSON. But I assume you have reason to be doing it this way.
You said:
If there's a better way I should be thinking about this, let me know.
If you're going to change your datastructure, why not change it into something like this?
[
["Mexico", 100, 50, 80, 30, 20],
["UK", 70, 80, 70, 70, 80],
["US", 80, 70, 90, 70, 80],
]
It's a lot smaller and contains no redundancies.
If you want your structure to be as close as possible to the data structure of the code example, I would nevertheless recommend sticking with your original data structure, as the CSV file in the original data structure is converted to a data structure pretty much identical to the data structure of your original JSON file.

Categories