how to get max value from a nested json array - javascript

I have a nested json array and I am trying to get the maximum value of the points attribute in this array.
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
I want the max value of points from under the children sections. I mean from under technology and oil sectors.
What I've done so far:
var max;
for (var i in data.children.length) {
for (var j in data.data[i]) {
var point = data.data[i].children[j]
}
}

Try the following:
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
var array = [];
for (var first of data.children) {
for (var second of first.children) {
if(second.points != undefined)
{
array.push(second);
}
}
}
var maximumValue = Math.max.apply(Math, array.map(function(obj) { return obj.points; }));
console.log(maximumValue);

you can use the reduce method on the array object to do this
const maxValues = []
data.children.forEach(el => {
if (el.name === 'OIL' || el.name === 'TECHNOLOGY & COMMUNICATIO'){
const max = el.children.reduce((current, previous) => {
if (current.points > previous.points) {
return current
}
}, 0)
maxValues.append({name: el.name, value: max.points})
}
})
This will give you an array of the objects with the name and max value.

First you can convert your object to a string through JSON.stringify so that you're able to use a regular expression
(?<=\"points\":)-?\\d*
To matchAll the values preceded by the pattern \"points\": that are or not negative values. After it, convert the result to a array through the spread operator ... and then reduce it to get the max value.
const data = {name:"KSE100",children:[{name:"TECHNOLOGY & COMMUNICATION",children:[{name:"TRG",points:-21},{name:"SYS"}]},{name:"OIL",children:[{name:"PPL",points:9},{name:"PSO",points:-19}]}]};
console.log(
[ ...JSON.stringify(data).matchAll('(?<=\"points\":)-?\\d*')]
.reduce((acc, curr) => Math.max(curr, acc))
)

I wasn't 100% sure, what your exact goal is, so I included a grouped max value and and overall max value with a slight functional approach.
Please be aware that some functionalities are not working in older browsers i.e. flatMap. This should anyways help you get started and move on.
const data = {
name: "KSE100",
children: [
{
name: "TECHNOLOGY & COMMUNICATION",
children: [
{
name: "TRG",
points: -21,
},
{
name: "SYS",
},
],
},
{
name: "OIL",
children: [
{
name: "PPL",
points: 9,
},
{
name: "PSO",
points: -19,
},
],
},
],
};
const maxPointsByGroup = data.children.reduce(
(acc, entry) => [
...acc,
{
name: entry.name,
max: Math.max(
...entry.children
.map((entry) => entry.points)
.filter((entry) => typeof entry === "number")
),
},
],
[]
);
console.log("grouped max:", maxPointsByGroup);
const overallMax = Math.max(
...data.children
.flatMap((entry) => entry.children.flatMap((entry) => entry.points))
.filter((entry) => typeof entry === "number")
);
console.log("overall max:", overallMax);

Related

NaN after adding particular key element in an Array object

I have an Array object with 3000 objects. Among these 3000 few of them have grade and few object doesn't. Now I want to sum the grades. I'm getting NaN. Could you please guide me what am I doing wrong. Below is the sample code:
const arr=[
{
"name":"Harvey",
"grade":3
},
{
"name":"Pamela",
},
{
"name":"Scott",
"grade":4
},
{
"name":"Joshua",
"grade":5
},{
"name":"Rachel",
},{
"name":"Harvey",
"grade":3
},
]
let classTotal = arr.reduce(function (previousValue, currentValue) {
return {
grade: (previousValue.grade + currentValue.grade)
}
})
console.log(classTotal) //NaN
Also tried the following:
let classTotal=arr.reduce((accum, item) => accum + item.total, 0)
console.log(classTotal) // Same NaN
If either of the .grade values is itself not a number (such as undefined) then it will break the ongoing calculations. One approach could be to default it to 0 when no value is present. So instead of currentValue.grade you might use (currentValue.grade ?? 0). For example:
const arr=[
{
"name":"Harvey",
"grade":3
},
{
"name":"Pamela",
},
{
"name":"Scott",
"grade":4
},
{
"name":"Joshua",
"grade":5
},
{
"name":"Rachel",
},
{
"name":"Harvey",
"grade":3
},
];
let classTotal = arr.reduce(function (previousValue, currentValue) {
return {
grade: (previousValue.grade + (currentValue.grade ?? 0))
};
});
console.log(classTotal);
NaN is "Not a valid Number" you have some entries missing grade you should run filter to filter them out before your reduce
const arr = [{
"name": "Harvey",
"grade": 3
},
{
"name": "Pamela",
},
{
"name": "Scott",
"grade": 4
},
{
"name": "Joshua",
"grade": 5
}, {
"name": "Rachel",
}, {
"name": "Harvey",
"grade": 3
},
]
let classTotal = arr.filter(function(element) {
return element.grade
}).reduce(function(previousValue, currentValue) {
return {
grade: (previousValue.grade + currentValue.grade)
}
})
console.log(classTotal)
Or, you can add a 0 for example for the elements who does not have a grade:
const arr = [{
"name": "Harvey",
"grade": 3
},
{
"name": "Pamela",
},
{
"name": "Scott",
"grade": 4
},
{
"name": "Joshua",
"grade": 5
}, {
"name": "Rachel",
}, {
"name": "Harvey",
"grade": 3
},
]
let classTotal = arr.reduce(function(previousValue, currentValue) {
return {
grade: (previousValue.grade + (currentValue.grade || 0))
}
})
console.log(classTotal)

Return additional properties using reduce and spread operator JS?

I am using the reduce function below to count how many times a players name is mentioned and then list them based on who was mentioned the most to the least.
I am trying to return the 2nd property [`${value.subtitles[0].name} + ${index}`] : value.subtitles[0].url with my object and sort it. However it is not sorting properly. When only returning the first property [value.title]: (acc[value.title] || 0) + 1, everything works as intended. But the second property is making it sort incorrectly. It is supposed to be sorting based on the title property value which is an integer of how many times that player was mentioned, from most to least. Why is this happening?
Thanks for the help!
const players = [
{
"title": "Mike",
"titleUrl": "https://mikegameplay",
"subtitles": [
{
"name": "Mike Channel",
"url": "https://channel/mike"
}
]
},
{
"title": "Cindy",
"titleUrl": "https://cindy",
"subtitles": [
{
"name": "Cindy Channel",
"url": "https://channel/cindy"
}
]
},
{
"title": "Mike",
"titleUrl": "https://mike",
"subtitles": [
{
"name": "Mike Channel",
"url": "https://channel/mike"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
}
]
const counts = players.reduce((acc, value, index) => ({
...acc,
[value.title]: (acc[value.title] || 0) + 1,
[`${value.subtitles[0].name} + ${index}`] : value.subtitles[0].url
}), {});
const sortedValues = [];
for (const value in counts) {
sortedValues.push([value, counts[value]]);
};
sortedValues.sort((a, b) => b[1] - a[1]);
console.log(sortedValues)
try this
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var pl = groupBy(players, "title");
console.log(pl);
let sortable = [];
for (var item in pl) {
sortable.push([item, pl[item].length, pl[item][0].subtitles[0].url]);
}
sortable.sort(function (a, b) {
return b[1] - a[1];
});
console.log(JSON.stringify(sortable));
result
[["Haley",3,"https://channel/haley"],["Mike",2,"https://channel/mike"],["Cindy",1,"https://channel/cindy"]]

Is there a simple way to transform this JSON back and forth?

We are using Postman for our API testing. Some object we are getting back are very verbose and not easy to handle, so I want to create a helper method to make them a bit more concise. I know there are all kind of transformation libraries like node-json-transform, selecttransform, jsontransforms, etc., but unfortunately I can only use the Postman Sandbox libraries and vanilla JS.
I am looking for the simplest (least amount of loc and functions) way to transform this object:
var verbose = [
{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}];
into this:
var concise = {
"Name": "John Doe",
"Age": "33",
"Child": ["Jane", "Rocky"]
};
and back again into the verbose form.
I already tried the native way of foreach-ing over each object and adding properties/values to a new object, but it went ugly soon when I reached the multiple instance key/value pairs. I can imagine there is an easier way using map/reduce but I am unfamiliar with those methods.
Based on how I've understood your question, you want to create key-value pairs from your verbose array of objects. However, if there are key clashes, then the values should be converted into an array.
With that in mind, you will have to:
Use forEach to loop through your array of objects.
If key does not clash, we simply create a new key-value pair
If key clashes, then it gets a bit tricky:
If key clashes and this is the first occurrence, we convert the value in the key-value pair into an array
If key clashes and this is not the first occurrence, we know we are looking at an array
Now we definitely has an array, so we push our value into it
See proof-of-concept below:
var verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}];
var concise = {};
verbose.forEach(function(i) {
var key = i['Key'];
var value = i['Value'];
// If item exists, we want to convert the value into an array of values
if (key in concise) {
var item = concise[key];
// If it is not an array already, we convert it to an array
if (!Array.isArray(item))
item = [item];
item.push(value);
concise[key] = item;
}
// If item does not exist, we simply create a new key-value pair
else {
concise[key] = value;
}
});
console.log(concise);
Here, I assume all attributes are multivalued, then I reduce those that have length 1 to a simple value. This is a bit slower than the reverse approach, where you assume values are singlevalued and promote them to arrays when they prove otherwise, in order to respect the ordering imposed by Instance.
function makeConcise(verbose) {
let concise = {};
verbose.forEach(({Key, Value, Instance}) => {
if (!concise[Key]) concise[Key] = [];
concise[Key][Instance - 1] = Value;
});
Object.keys(concise).forEach(Key => {
if (concise[Key].length == 1) concise[Key] = concise[Key][0];
});
return concise;
}
The reverse function is similarly simple:
function makeVerbose(concise) {
let verbose = [];
Object.keys(concise).forEach(Key => {
if (Array.isArray(concise[Key])) {
concise[Key].forEach((Value, index) => {
verbose.push({Key, Value, Instance: index + 1});
});
} else {
verbose.push({Key, Value: concise[Key], Instance: 1});
}
});
return verbose;
}
const verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}
];
let concise = {};
verbose.forEach(item => {
const values = Object.values(item)
if (concise[values[0]]) concise = {...concise, [values[0]]: [concise[values[0]], values[1]]};
else concise = {...concise, ...{[values[0]]: values[1]}}
})
Try this. I have written both conversion functions.
I see other answers only provide only verbose to concise requirement.
let verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}
]
let concise = {
"Name": "John Doe",
"Age": "33",
"Child": ["Jane", "Rocky"]
}
verboseToConcise = (verbose) => {
let obj = {}
verbose.forEach(v => {
let key = obj[v.Key]
if (key) typeof key === 'string' ? obj[v.Key] = [key, v.Value] : key.push(v.Value)
else obj[v.Key] = v.Value
})
return obj
}
conciseToVerbose = (concise) => {
let arr = []
Object.entries(concise).forEach(([key, value]) => {
if (typeof value === 'object') {
for (let i = 0; i < value.length; i++){
arr.push({
"Key": key,
"Value": value[i],
"Instance": i+1
})
}
} else {
arr.push({
"Key": key,
"Value": value,
"Instance": 1
})
}
})
return arr
}
console.log(verboseToConcise(verbose))
console.log(conciseToVerbose(concise))
You can do:
const verbose = [{"Key": "Name","Value": "John Doe","Instance": 1},{"Key": "Age","Value": "33","Instance": 1},{"Key": "Child","Value": "Jane","Instance": 1},{"Key": "Child","Value": "Rocky","Instance": 2}];
const concise = Object.values(verbose.reduce((a, {Key, Value}) => (Key === 'Child' ? a.childs[0].Child.push(Value) : a.keys.push({[Key]: Value}), a), {keys: [], childs: [{Child: []}]})).flat(1);
console.log(concise);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I also gave it a try using reduce:
EDIT: Without ... spread syntax, with Object.assign and array.concat
EDIT2: I wanted to try and turn it back again. In this code we lose the value of Instance:(
var verbose = [
{
Key: 'Name',
Value: 'John Doe',
Instance: 1,
},
{
Key: 'Age',
Value: '33',
Instance: 1,
},
{
Key: 'Child',
Value: 'Jane',
Instance: 1,
},
{
Key: 'Child',
Value: 'Rocky',
Instance: 2,
},
]
const concise = verbose.reduce(
(p, n) =>
Object.assign(p, {
[n.Key]: !p.hasOwnProperty(n.Key)
? n.Value
: typeof p[n.Key] === 'string'
? [p[n.Key], n.Value]
: p[n.Key].concat(n.Value),
}),
{},
)
console.log(concise)
// { Name: 'John Doe', Age: '33', Child: [ 'Jane', 'Rocky' ] }
const backAgain = Object.entries(concise).reduce(
(p, [k, v]) =>
Array.isArray(v)
? p.concat(v.map(x => ({ Key: k, Value: x })))
: p.concat({ Key: k, Value: v }),
[],
)
console.log(backAgain)
// [ { Key: 'Name', Value: 'John Doe' },
// { Key: 'Age', Value: '33' },
// { Key: 'Child', Value: 'Jane' },
// { Key: 'Child', Value: 'Rocky' } ]

Javascript underscore data format array of object

By using underscoreJS lib and manipulating some datas, i have this object
var data = {
"2017-09-26": [
{
"id": 274281,
"value": 10
},
{
"id": 274282,
"value": 20
}],
"2017-09-27": [
{
"id": 274281,
"value": 12
},
{
"id": 274282,
"value": 13
}],
}
i would like to obtain this result below by passing the keys as date in the child object and transform the value of id key as the new key of the value of value
var data = [{
date:"2017-09-26",
274281: 10,
274282: 20
},
{
date:"2017-09-27",
274281: 12,
274282: 13
}]
Please does someone as an idea to help me to do this and ideally efficiently?
Thanks
Here it is in one line:
Object.keys(data).map(key => ({date: key, ...data[key].reduce((p, c) => {p[c.id] = c.value; return p}, {})}))
Result:
[{
"274281":10,
"274282":20,
"date":"2017-09-26"
},
{
"274281":12,
"274282":13,
"date":"2017-09-27"
}]
You need nested loops. The first level creates the objects with the date property, then you loop over the objects in that value, and add the id: value properties to the result.
var newdata = _.map(data, (date, objects) => {
res = {date: date};
_.each(objects, obj => {
res[obj.id] = obj.value;
});
return res;
});
You can use Array.from() on the result of Object.entries(data) to create an array of objects by passing a callback function as the second argument.
Then for each sub-array, use .reduce() to create a new object from its members.
var data = {
"2017-09-26": [
{ "id": 274281, "value": 10 },
{ "id": 274282, "value": 20 }
],
"2017-09-27": [
{ "id": 274281, "value": 12 },
{ "id": 274282, "value": 13 }
],
};
const result = Array.from(Object.entries(data), ([key, arr]) =>
arr.reduce((res, {id, value}) =>
Object.assign(res, {[id]: value})
, {date: key})
);
console.log(result);
Here's one that's just about the same, but uses the new object literal spread syntax.
var data = {
"2017-09-26": [
{ "id": 274281, "value": 10 },
{ "id": 274282, "value": 20 }
],
"2017-09-27": [
{ "id": 274281, "value": 12 },
{ "id": 274282, "value": 13 }
],
};
const result = Array.from(Object.entries(data), ([key, arr]) =>
({date: key,
...Object.assign({}, ...arr.map(({id, value}) => ({[id]: value})))
})
);
console.log(result);

modify json object and save it

I do have a json array object. I need to modify it then save the modified version on a variable.
the json object
var json = [
{
"Name": "March-2016",
"Elements": [
{
"Name": "aa",
"Elements": [
{
"Name": "ss",
"Data": {
"Test1": [
22
],
"Test2": [
33
],
"Test3": [
44
],
"Test4": [
55
]
}
},
{
"Name": "ssee",
"Data": {
"Test12": [
222
],
"Test22": [
3322
],
"Test32": [
445
],
"Test42": [
553
]
}
}
]
}
]
}
];
need to be modified to
var json = [
{
"Name": "March-2016",
"Elements": [
{
"Name": "aa",
"Elements": [
{
"category": "ss",
"Test1": 22,
"Test2": 33 ,
"Test3":44,
"Test4": 55
},
{
"category": "ssee",
"Test12": 222,
"Test22": 3322 ,
"Test32":445,
"Test42": 553
}
]
}
]
}
];
I have this method but its not doing the job
var saveJson = function(arr) {
var nameValuePairs = [];
for (var i = 0, len = arr.length; i < len; i++) {
var item = arr[i];
if (item.Data) {
var newvar = {
category : item.Name
}
newvar[Object.keys(item.Data)] = Object.values(item.Data);
item = newvar
}
if (item.Elements) {
nameValuePairs = nameValuePairs.concat(saveJson(item.Elements));
}
}
return arr;
};
I need this conversion to be dynamic as for sure I will get bigger json than the posted one
sorry for the confusion and thanks in advance.
The original object is really a mess, but still you just need to step through it and pull out the values you want. This changes the json object in place:
json.forEach(item => {
item.Elements.forEach(Outer_El => {
Outer_El.Elements = Outer_El.Elements.map(item =>{
let obj = {category: item.Name}
Object.keys(item.Data).forEach(key => {
obj[key] = item.Data[key][0]
})
return obj
})
})
})
json should now look like:
[{
"Name":"March-2016",
"Elements":[
{
"Name":"aa",
"Elements":[
{
"category":"ss",
"Test1":22,
"Test2":33,
"Test3":44,
"Test4":55
},
{
"category":"ssee",
"Test1e":224,
"Test2e":334,
"Test3e":443,
"Test4e":554
}
]
}
]
}]
You can use destructuring assignment to get specific property values from an object
{
let {Name:categories, Data:{Test:[Test]}} = json[0].Elements[0].Elements[0];
json[0].Elements[0].Elements[0] = {categories, Test};
}

Categories

Resources