Merge arrays matching a particular key value in JavaScript - javascript

I have an array which is like this:
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
I want to match the primary key which is heredate. Matching this I want to merge the rest of the key values and get this following output:
[{
"date": "JAN",
"value": [5, 4],
"weight": [3, 23]
}, {
"date": "FEB",
"value": [9, 10],
"weight": [1, 30]
}]
I have written a function like this but can't figure out how to concat the key values:
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
const transform = (arr, primaryKey) => {
var newValue = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 1; j < arr.length; j++) {
if (primaryKey[i] === primaryKey[j]) {
newValue.push({
...arr[i],
...arr[j]
});
}
}
}
return newValue
};
console.log(transform(arr,'date'))

Using Array#reduce, iterate over the list while updating a Map where the key is the primary-key and the value is the grouped object. In every iteration, create/update the pair.
Using Map#values, return the list of grouped objects
const transform = (arr, primaryKey) => [...
arr.reduce((map, { [primaryKey]: key, ...e }) => {
const { [primaryKey]: k, ...props } = map.get(key) ?? {};
for(let prop in e) {
props[prop] = [...(props[prop] ?? []), e[prop]];
}
map.set(key, { [primaryKey]: key, ...props });
return map;
}, new Map)
.values()
];
const arr = [ { "date": "JAN", "value": 5, "weight": 3 }, { "date": "JAN", "value": 4, "weight": 23 }, { "date": "FEB", "value": 9, "weight": 1 }, { "date": "FEB", "value": 10, "weight": 30 } ];
console.log( transform(arr, 'date') );

The following code should work:
const transform = (arr, primaryKey) => {
var newValue = [];
for(let i = 0; i < arr.length; i++){
arr[i]["value"] = [arr[i]["value"]];
arr[i]["weight"] = [arr[i]["weight"]];
}
newValue.push(arr[0])
for(let i = 1; i < arr.length; i++){
let contains = false;
for(let j = 0; j < newValue.length; j++){
if(newValue[j][primaryKey] == arr[i][primaryKey]){
newValue[j]["value"].push(arr[i]["value"][0]);
newValue[j]["weight"].push(arr[i]["weight"][0]);
contains = true;
}
}
if(!contains){
newValue.push(arr[i]);
}
}
return newValue
};
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
var newthing = transform(arr,"date");
console.log(newthing);
Output:
[ { date: 'JAN', value: [ 5, 4 ], weight: [ 3, 23 ] },
{ date: 'FEB', value: [ 9, 10 ], weight: [ 1, 30 ] } ]
The way this code works is that first, we turn the values of the keys for "value" and "weight" into lists.
Then, we begin by pushing the first element of arr into newValue.
From here, we do a nested for loop to iterate through the remaining of arr and newValue:
If the value of "date" for every element of arr already exists in newValue, then we will push in the values of "value" and "weight" that belongs to arr.
However, if it does not exist, then we will simply push that element inside of newValue.
I hope this helped answer your question! Pleas let me know if you need any further help or clarification :)

Combining a couple of reduce can also do the same job:
const arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
const arrayMappedByDate = arr.reduce((acc, curData) => {
if (acc[curData.date]) {
acc[curData.date].push(curData)
} else {
acc[curData.date] = [curData]
}
return acc
}, {})
const transformedArray = Object.entries(arrayMappedByDate).map(([dateInit, data]) => {
const normalized = data.reduce((acc, cur) => {
if (acc.date) {
acc.value.push(cur.value)
acc.weight.push(cur.weight)
} else {
acc = {
date: cur.date,
value: [cur.value],
weight: [cur.weight]
}
}
return acc
}, {})
return { [dateInit]: normalized }
})
console.log(transformedArray)

Related

Build min and max dynamically with keys

Let's assume we have the following data entries:
const data = [{
"id": "0",
"name": {
"first": "",
"last": ""
},
"nickname": "test",
"rating": {
"kw": 1,
"dc": 2,
"imp": 3,
"pat": 4
}
},
{
"id": "1",
"name": {
"first": "",
"last": ""
},
"nickname": "test2",
"rating": {
"kw": 28,
"dc": 26,
"imp": 27,
"pat": 14
}
},
{
"id": "2",
"name": {
"first": "",
"last": ""
},
"nickname": "test3",
"rating": {
"kw": 11,
"dc": 8,
"imp": 9,
"pat": 1
}
}
];
I don't know these object keys within rating, so the object could also look like:
{
"id": "1",
"name": {
"first": "",
"last": ""
},
"nickname": "test2",
"rating": {
"ab": 28,
"cd": 26,
"moep": 27,
"bla": 14
}
}
I would like to do the following:
Generate a new object based on the data object keys dynamically (as I don't know them)
Create a subobject {min: xx, max: xx} for all those object keys
So the result should look like this:
{
kw: {
min: 1,
max: 28
},
dc: {
min: 2,
max: 26
},
imp: {
min: 3,
max: 27
},
pat: {
min: 1,
max: 14
},
}
How can I achieve this ?
You can reduce the array, and use Array.forEach() to iterate the entries, and create/populate the min and max values of each rating key:
const data=[{id:"0",name:{first:"",last:""},nickname:"test",rating:{kw:1,dc:2,imp:3,pat:4}},{id:"1",name:{first:"",last:""},nickname:"test2",rating:{kw:28,dc:26,imp:27,pat:14}},{id:"2",name:{first:"",last:""},nickname:"test3",rating:{kw:11,dc:8,imp:9,pat:1}}];
const result = data.reduce((r, { rating }) => {
Object.entries(rating).forEach(([k, v]) => {
if(!r[k]) r[k] = { min: v, max: v }; // if doesn't exist min = max = current value
else if(v < r[k].min) r[k].min = v; // if current value is less than min -> min = current value
else if(v > r[k].max) r[k].max = v; // if current value is more than max -> max = current value
});
return r;
}, {});
console.log(result);
You could reduce the array. Loop through the keys of rating and set the min and max for each key
const data=[{id:"0",name:{first:"",last:""},nickname:"test",rating:{kw:1,dc:2,imp:3,pat:4}},{id:"1",name:{first:"",last:""},nickname:"test2",rating:{kw:28,dc:26,imp:27,pat:14}},{id:"2",name:{first:"",last:""},nickname:"test3",rating:{kw:11,dc:8,imp:9,pat:1}}];
const output = data.reduce((acc, { rating }) => {
for (const key in rating) {
const value = rating[key];
acc[key] = acc[key] || { min: value, max: value }; // if key doesn't exist, add it
acc[key].min = Math.min( acc[key].min, value )
acc[key].max = Math.max( acc[key].max, value )
}
return acc;
}, {})
console.log(output)

Maximum value from the JSON

I needed the maximum value from the particular JSON object. I needed it to make a scale for apple and banana's for d3 from its maximum value
So how do I get the maximum value of apple and banana from this JSON. I don't need the month name. I don't want to use any for or while loops.
var arr = [
{
"date":"Jan",
"values": [
{"name":"apple","value":100},
{"name":"banana","value":200}
]
},
{
"date":"Feb",
"values": [
{"name":"apple","value":300},
{"name":"banana","value":455}
]
},
{
"date":"Mar",
"values": [
{"name":"apple","value":588},
{"name":"banana","value":700}
]
}
];
You can also use Math.max after finding the fruit from values with find:
var arr = [{
"date": "Jan",
"values": [{
"name": "apple",
"value": 100
},
{
"name": "banana",
"value": 200
}
]
},
{
"date": "Feb",
"values": [{
"name": "apple",
"value": 300
},
{
"name": "banana",
"value": 455
}
]
},
{
"date": "Mar",
"values": [{
"name": "apple",
"value": 588
},
{
"name": "banana",
"value": 700
}
]
}
];
const maxVal = (fruit) => Math.max(...arr.map(d => d.values.find(v => v.name === fruit).value));
console.log(maxVal('apple'));
console.log(maxVal('banana'));
You could something like this:
const max = (list) => list.reduce((acc, cur) => {
let apple = cur.values[0].name === 'apple' ? cur.values[0] : cur.values[1];
let banana = cur.values[0].name === 'banana' ? cur.values[0] : cur.values[1];
acc[0] = acc[0] < apple.value ? apple.value : acc[0];
acc[1] = acc[1] < banana.value ? banana.value : acc[1];
return acc;
}, [0, 0]);
It returns an array, the first item is the max number of apples, the second is the max number of bananas.
Example
var arr = [{
"date": "Jan",
"values": [{
"name": "apple",
"value": 100
},
{
"name": "banana",
"value": 200
}
]
},
{
"date": "Feb",
"values": [{
"name": "apple",
"value": 300
},
{
"name": "banana",
"value": 455
}
]
},
{
"date": "Mar",
"values": [{
"name": "apple",
"value": 588
},
{
"name": "banana",
"value": 700
}
]
}
];
const max = (list) => list.reduce((acc, cur) => {
let apple = cur.values[0].name === 'apple' ? cur.values[0] : cur.values[1];
let banana = cur.values[0].name === 'banana' ? cur.values[0] : cur.values[1];
acc[0] = acc[0] < apple.value ? apple.value : acc[0];
acc[1] = acc[1] < banana.value ? banana.value : acc[1];
return acc;
}, [0, 0]);
console.log(
max(arr)
);

Javascript object sum values of same property key

I Have the following object and I want to sum the values of the same ingredients name, like: tomato: 5, chicken:5, rice: 1, peas: 1, i have this code but is not making the sum, instead is showing all the objects
var data3 = {
"menus": [{
"recipe": "chicken with rice",
"ingredients": [{
"name": "tomato",
"value": 2
}, {
"name": "chicken",
"value": 3
}, {
"name": "rice",
"value": 1
}]
}, {
"recipe": "Garden rice",
"ingredients": [{
"name": "tomato",
"value": 3
}, {
"name": "chicken",
"value": 2
}, {
"name": "peas",
"value": 1
}]
}]
};
var ingredients;
for (var i = 0; i < 10; i++) {
ingredients = data3.menus[i].ingredients;
var temp = {};
var obj = null;
for (var j = 0; j < ingredients.length; j++) {
obj = ingredients[j];
if (!temp[obj.name]) {
temp[obj.name] = obj;
} else {
temp[obj.name].value += obj.value;
}
}
var result = [];
for (var prop in temp)
result.push(temp[prop]);
console.log(result);
};
I will appreciate your help, thanks!
You could use a hash table with the name of the ingredient and use a default value of zero if a property does not exist. Then add the value.
var data3 = { menus: [{ recipe: "chicken with rice", ingredients: [{ name: "tomato", value: 2 }, { name: "chicken", value: 3 }, { name: "rice", value: 1 }] }, { recipe: "Garden rice", ingredients: [{ name: "tomato", value: 3 }, { name: "chicken", value: 2 }, { name: "peas", value: 1 }] }] },
ingredients = Object.create(null);
data3.menus.forEach(function (a) {
a.ingredients.forEach(function (b) {
ingredients[b.name] = (ingredients[b.name] || 0) + b.value;
});
});
console.log(ingredients);
Your implementation is correct, just you were re-initializing temp again and again. So you just need to take temp out of your for loop.
var data3 = {
"menus": [{
"recipe": "chicken with rice",
"ingredients": [{
"name": "tomato",
"value": 2
}, {
"name": "chicken",
"value": 3
}, {
"name": "rice",
"value": 1
}]
}, {
"recipe": "Garden rice",
"ingredients": [{
"name": "tomato",
"value": 3
}, {
"name": "chicken",
"value": 2
}, {
"name": "peas",
"value": 1
}]
}]
};
var ingredients;
var temp = {};
for (var i = 0; i < data3.menus.length; i++) {
ingredients = data3.menus[i].ingredients;
var obj = null;
for (var j = 0; j < ingredients.length; j++) {
obj = ingredients[j];
if (!temp[obj.name]) {
temp[obj.name] = obj;
} else {
temp[obj.name].value += obj.value;
}
}
};
var result = [];
for (var prop in temp)
result.push(temp[prop]);
console.log(result);
You can create a function that will traverse all ingredients and if ingredient.name has given name, then add ingredient.value to your result. Here is ES2015 solution with arrow functions:
const sumIngredients = (menus, name) => {
let result = 0;
menus.forEach(meal => {
meal.ingredients.forEach(ingredient => {
if (ingredient.name === name) {
result += ingredient.value;
}
})
})
return result;
}
The use it:
sumIngredients(data3.menus, 'chicken'); //5
sumIngredients(data3.menus, 'tomato'); //5
sumIngredients(data3.menus, 'rice'); //1
sumIngredients(data3.menus, 'peas'); //1
Working example: https://jsfiddle.net/sc8wrupk/

d3js roll up a nested JSON array and sum up a total value

I have this JSON
[{
"month": "september",
"detail": [{
"date": "01-09",
"value": 5
}, {
"date": "02-09",
"value": 5
}, {
"date": "03-09",
"value": 5
}, {
"date": "04-09",
"value": 5
}, {
"date": "05-09",
"value": 5
}, {
"date": "06-09",
"value": 5
}, {
"date": "07-09",
"value": 0
}]
},
{
"month": "october",
"detail": [{
"date": "01-10",
"value": 10
}, {
"date": "02-10",
"value": 5
}, {
"date": "03-10",
"value": 5
}, {
"date": "04-10",
"value": 5
}, {
"date": "05-10",
"value": 5
}, {
"date": "07-10",
"value": 10
}]
}
I want to roll up the all the "value" in the object "detail" for each specific month using d3nest. If you count all the values for each specific months it would result in: september-value: 30 & october-value: 40.
I've tried nesting it but I can't get it right to sum up the values for each month. see my code.
d3.json('runningdata.json', function (error, data) {
console.log(data);
var Total = d3.nest()
.key(function(d) { return d.month; })
.rollup(function(value) { return d3.sum(value, function(v) { return v.detail.value; }); })
.entries(data);
console.log(JSON.stringify(Total));
});
All of this above would result in:
[{"key":"september","values":0},{"key":"october","values":0}]
You can notice the "key" are working right, they have the month as value. but the "Values" field result in 0. what i am trying to achieve needs to be:
[{"key":"september","values":30},{"key":"october","values":40}]
But when i try this:
.rollup(function(value) { return d3.sum(value, function(v) { return v.detail[0].value; }); })
instead of:
.rollup(function(value) { return d3.sum(value, function(v) { return v.detail.value; }); })
it shows me the values of the first object in the arrays.
[{"key":"september","values":5},{"key":"october","values":10}]
What am i doing wrong? I have been reading about d3 nesting.
Any help is welcome.
Note:
Im trying achieve this to make a graph which presents the total of each months and when you click on the specific month it will view the details of its month in days.
You have to update your d3.sum function like so:
d3.sum(value[0].detail, function(v) {
return v.value;
});
So your whole code would be:
var Total = d3.nest()
.key(function(d) {
return d.month;
})
.rollup(function(value) {
return d3.sum(value[0].detail, function(v) {
return v.value;
});
})
.entries(data);
You can just use basic javascript .reduce() function to do the trick. This will roll up your array of object structure into a single object with the months as the property name and the sum of the values as the value. The advantage to doing it this way is that if you want to get, say, the total for the month of September, you can simply say combined.september, rather than having to iterate through an array, searching for the object with the key property equal to "september" and then extracting the value. However, if you need to keep your original structure, see the slightly modified version at the bottom of my answer.
var data = [{
"month": "september",
"detail": [{
"date": "01-09",
"value": 5
}, {
"date": "02-09",
"value": 5
}, {
"date": "03-09",
"value": 5
}, {
"date": "04-09",
"value": 5
}, {
"date": "05-09",
"value": 5
}, {
"date": "06-09",
"value": 5
}, {
"date": "07-09",
"value": 0
}]
},
{
"month": "october",
"detail": [{
"date": "01-10",
"value": 10
}, {
"date": "02-10",
"value": 5
}, {
"date": "03-10",
"value": 5
}, {
"date": "04-10",
"value": 5
}, {
"date": "05-10",
"value": 5
}, {
"date": "07-10",
"value": 10
}]
}];
var combined = data.reduce(function (total, current){
total[current.month] = current.detail.reduce(function(tot, curr) {
return tot + curr.value;
}, 0);
return total;
}, {});
console.log(combined);
Edit -- if you need to keep your original {"key": "..", "values", ".."} structure
You can use .map() rather than .reduce() like so:
var combined = data.map(function (elem){
var total = elem.detail.reduce(function(total, current) {
return total + current.value;
}, 0);
return {key: elem.month, values: total};
});
console.log(combined);
// output:
// [ { key: 'september', value: 30 },
// { key: 'october', value: 40 } ]

How to remove parent fields in json variable and convert it to json in javascript or jquery

Am working on some project which has lot of objects involvement. I have below requirement in javascript or jQuery.
I have below object:
var dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
How can I remove parent fields and make it as object like below ?
var d= [
{ "id": 0, "name": "Housing", "value": 18 },
{ "id": 1, "name": "Travel", "value": 31.08 },
{ "id": 2, "name": "Restaurant", "value": 64 },
{ "id": 3, "name": "Bank", "value": 3 },
{ "id": 4, "name": "Movies", "value": 10 }
]
After doing this, I wanted to pass this object to a javascript function which manipulates the value of this variable d.
if(value >= 10 && value <= 20) {
d[index].value = 7;
}
if(value >= 20 && value <= 40) {
d[index].value = 8;
}
Updated object should look like :
var d= [
{ "id": 0, "name": "Housing", "value": 7 },
{ "id": 1, "name": "Travel", "value": 8 },
{ "id": 2, "name": "Restaurant", "value": 64 },
{ "id": 3, "name": "Bank", "value": 3 },
{ "id": 4, "name": "Movies", "value": 10 }
]
I tried looping to the dataset using for loop, but couldn't achieve my requirement.
Any help?
Use Array#forEach with Object.keys() to iterate over object.
var dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
// Declare resulting empty array
var d = [];
// Get object keys and iterate over them
Object.keys(dataset).forEach(function (key) {
// Get the value from the object
var value = dataset[key].value;
// Update values if in the range
if(value >= 10 && value <= 20) {
dataset[key].value = 7;
} else if(value > 20 && value <= 40) {
dataset[key].value = 8;
}
// Push the updated(or not) value in the array
d.push(dataset[key]);
});
console.log(d);
document.getElementById('result').innerHTML = JSON.stringify(d, null, 4);
<pre id="result"></pre>
Just map it with Array.prototype.map method:
var dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
var d = Object.keys(dataset).map(function(key) {
var obj = dataset[key];
if (obj.value >= 10 && obj.value <= 20) {
obj.value = 7;
}
else if (obj.value >= 20 && obj.value <= 40) {
obj.value = 8;
}
return obj;
});
document.write('<pre>' + JSON.stringify(d, null, 4) + '</pre>');
You can do it this way:
var dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
First remove the string keys:
var d = [];
for(var k in dataset){
d.push(dataset[k]);
}
Then iterate over resulting array modifying it accordingly:
for(var k in d){
var value = d[k]['value'];
if(value>=10 && value<=20){
d[k].value=7;
}
if(value>=20 && value<=40){
d[k].value=8;
}
}
This gives the resuls you're looking for:
var dataset = {
"d0": { "id": 0, "name": "Housing", "value": 18 },
"d1": { "id": 1, "name": "Travel", "value": 31.08 },
"d2": { "id": 2, "name": "Restaurant", "value": 64 },
"d3": { "id": 3, "name": "Bank", "value": 3 },
"d4": { "id": 4, "name": "Movies", "value": 10 }
};
var d = Object.keys(dataset).map(function(key) {
var v = dataset[key].value;
if (v >= 10 && v <= 20) dataset[key].value = 7;
else if (v > 20 && v <= 40) dataset[key].value = 8;
return dataset[key];
});
try this,
function datasetToArrOfObj(dataset){
var rslt = [];
for (var key in dataset) {
if (dataset.hasOwnProperty(key)) {
rslt.push(dataset[key]);
}
}
return rslt;
}

Categories

Resources