NaN after adding particular key element in an Array object - javascript

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)

Related

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"]]

how to get max value from a nested json array

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

JavaScript find highest version from array of dotted versions

I have a array of version numbers looking like this:
[
{
"name": "v12.3.0.pre",
},
{
"name": "v12.2.5",
},
{
"name": "v12.2.4",
},
{
"name": "v12.2.3",
},
{
"name": "v12.2.1",
},
{
"name": "v12.2.0",
},
{
"name": "v12.2.0.pre",
},
{
"name": "v12.2.0-rc32",
},
{
"name": "v12.2.0-rc31",
},
{
"name": "v12.1.9",
},
{
"name": "v12.1.8",
},
{
"name": "v12.1.6",
},
{
"name": "v12.1.4",
},
{
"name": "v12.1.3",
},
{
"name": "v12.1.2",
},
{
"name": "v12.1.1",
},
{
"name": "v12.1.0",
},
{
"name": "v12.1.0.pre",
},
{
"name": "v12.1.0-rc23",
},
{
"name": "v12.1.0-rc22",
},
{
"name": "v12.0.9",
},
{
"name": "v12.0.8",
},
{
"name": "v12.0.6",
},
{
"name": "v12.0.4",
},
{
"name": "v12.0.3",
},
{
"name": "v12.0.2",
},
{
"name": "v12.0.1",
},
{
"name": "v12.0.0",
},
{
"name": "v11.12.0.pre",
},
{
"name": "v11.11.8",
}
]
From this array I would like to determine the latest version, which do not end with '.pre' or include 'rc.
I'm iterating through the array with a for-loop, and filtering out the '.pre' and 'rc' with an if statement. I then use split/join to remove the first 'v' character. So far so good.
Then I'm left with values like '12.2.5' and '11.12.10'. I first thought of removing the dots, then use a 'greater than' operator to see find the highest value, but then '11.12.10(111210)' would result greater than '12.2.5(1225)' which would not work out in my case.
for(i in arr){
if(!arr[i].name.endsWith('.pre') && !arr[i].name.includes('rc')){
var number = number.split('v').join("");
var number = number.split('.').join("");
}
}
Any ideas on best way to solve this? Thanks!
You could take String#localeCompare with options for getting a result.
var data = [{ name: "v12.3.0.pre" }, { name: "v12.2.5" }, { name: "v12.2.4" }, { name: "v12.2.3" }, { name: "v12.2.1" }, { name: "v12.2.0" }, { name: "v12.2.0.pre" }, { name: "v12.2.0-rc32" }, { name: "v12.2.0-rc31" }, { name: "v12.1.9" }, { name: "v12.1.8" }, { name: "v12.1.6" }, { name: "v12.1.4" }, { name: "v12.1.3" }, { name: "v12.1.2" }, { name: "v12.1.1" }, { name: "v12.1.0" }, { name: "v12.1.0.pre" }, { name: "v12.1.0-rc23" }, { name: "v12.1.0-rc22" }, { name: "v12.0.9" }, { name: "v12.0.8" }, { name: "v12.0.6" }, { name: "v12.0.4" }, { name: "v12.0.3" }, { name: "v12.0.2" }, { name: "v12.0.1" }, { name: "v12.0.0" }, { name: "v11.12.0.pre" }, { name: "v11.11.8" }],
highest = data
.filter(({ name }) => !name.endsWith('.pre') && !name.includes('rc'))
.reduce((a, b) =>
0 < a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
? a
: b
);
console.log(highest);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think i have a solution for you: Save the numbers to arrays, so you have for the numbers '12.2.5' and '11.12.10' the following:
[12,2,5] and [11,12,10]
Then you compare the arrays. Keep the greates from the [0] position, if they're equal the greates from the [1] position and so...
Maybe it works...
Hope it helps you!!
Kind regards,
Sergio.
Here I am not going with the logic of removing and filtering out the '.pre' and 'rc' with an if statement. then use split/join to remove the first 'v' character. Then you left with the values like '11.12.10' and '12.2.5' so, after getting these values you can use below code
You could prepend all parts to fixed-size strings, then sort that, and finally remove the padding again
Below code, the snippet is an example not for above exactly but will help you sure
var arr = ['12.2.5', '11.12.10', '12.0.6', '6.1.0', '5.1.0', '4.5.0'];
arr = arr.map( a => a.split('.').map( n => +n+100000 ).join('.') ).sort()
.map( a => a.split('.').map( n => +n-100000 ).join('.') );
console.log(arr)
The basic idea to make this comparison would be to use Array.split to get arrays of parts from the input strings and then compare pairs of parts from the two arrays; if the parts are not equal we know which version is smaller. Reference
var versionsList = [
{ "name": "v12.3.0.pre" },
{ "name": "v12.2.5" },
{ "name": "v12.2.4" },
{ "name": "v12.2.3" },
{ "name": "v12.2.1" },
{ "name": "v12.2.0" },
{ "name": "v12.2.0.pre" },
{ "name": "v12.2.0-rc32" },
{ "name": "v12.2.0-rc31" },
{ "name": "v12.1.9" },
{ "name": "v12.1.8" },
{ "name": "v12.1.6" },
{ "name": "v12.1.4" },
{ "name": "v12.1.3" },
{ "name": "v12.1.2" },
{ "name": "v12.1.1" },
{ "name": "v12.1.0" },
{ "name": "v12.1.0.pre" },
{ "name": "v12.1.0-rc23" },
{ "name": "v12.1.0-rc22" },
{ "name": "v12.0.9", },
{ "name": "v12.0.8", },
{ "name": "v12.0.6", },
{ "name": "v12.0.4", },
{ "name": "v12.0.3", },
{ "name": "v12.0.2", },
{ "name": "v12.0.1", },
{ "name": "v12.0.0", },
{ "name": "v11.12.0.pre", },
{ "name": "v11.11.8", }
];
function versionCompare(v1, v2) {
var v1parts = v1.split('.'),
v2parts = v2.split('.');
for (var i=0; i<v1parts.length; i++) {
if (v1parts[i] === v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return v1;
}
else {
return v2;
}
}
return v1;
}
var maxVersion;
for (i in versionsList) {
version = versionsList[i].name;
if (!version.endsWith('.pre') && !version.includes('rc')) {
if (typeof maxVersion === "undefined")
maxVersion = version.substr(1);
var ver = version.substr(1);
if (ver !== maxVersion)
maxVersion = versionCompare(ver, maxVersion);
}
}
console.log('v'+ maxVersion);

Loop through possible arrays in json with typescript

I'm not asking how to loop through an array in typescript. My question is a bit different so let me explain first.
I have a json which looks like this:
{
"forename": "Maria",
"colors": [
{
"name": "blue",
"price": 10
},
{
"name": "yellow",
"price": 12
}
],
"items": [
{
"name": "sword",
"price": 20
}
],
"specialPowers": [
{
"name": "telekinesis",
"price": 34
}
]
},
{
"forename": "Peter",
"colors": [
{
"name": "blue",
"price": 10
}
],
"items": [
{
"name": "hat",
"price": 22
},
{
"name": "hammer",
"price": 27
}
]
}
// some more persons
As you can see, I have persons which can have arrays like colors, items or specialPowers. BUT a person can also have none of them. As you can see Maria has the array specialPowers, but Peter has not.
I need a function which checks if a person has one of these arrays and if so, I have to sum its price to a total. So I want the total price of all the things a person has.
At the moment I have three functions which basically look like this:
getTotalOfColors(person) {
let total = 0;
if(person.colors)
for (let color of person.colors) {
total = total + color.price;
}
return total;
}
getTotalOfItems(person) {
let total = 0;
if(person.items)
for (let item of person.items) {
total = total + item.price;
}
return total;
}
// SAME FUNCTION FOR SPECIALPOWERS
I basically have the same function for three times. The only difference is, that I'm looping through another array. But these functions do all the same. They first check, if the person has the array and secondly they loop through this array to add the price to a total.
Finally to my question: Is there a way to do this all in ONE function? Because they all are basically doing the same thing and I don't want redundant code. My idea would be to loop through all the arrays while checking if the person has the array and if so, adding its price to the total.
I assume the function would look something like this:
getTotal(person) {
let total = 0;
for (let possibleArray of possibleArrays){
if(person.possibleArray )
for (let var of person.possibleArray ) {
total = total + var.price;
}
}
return total;
}
Like this I would have a "universal" function but for that I have to have an array of the possible arrays like this: possibleArrays = [colors, items, specialPowers]
How do I achieve this? How and where in my code should I make this array ? Or is there even a better solution for this problem?
I created a function that seems to do the trick:
function totalPrice(data) {
let total = 0;
for (person of data) { //Go through the array of people
for (prop in person) { //Go through every property of the person
if (Array.isArray(person[prop])) { //If this property is an array
for (element of person[prop]) { //Go through this array
//Check if `price` is a Number and
//add it to the total
if (!isNaN(element.price)) total += element.price;
}
}
}
}
return total;
}
Demo:
function totalPrice(data) {
let total = 0;
for (person of data) {
for (prop in person) {
if (Array.isArray(person[prop])) {
for (element of person[prop]) {
if (!isNaN(element.price)) total += element.price;
}
}
}
}
return total;
}
let data = [
{
"forename": "Maria",
"colors": [{
"name": "blue",
"price": 10
},
{
"name": "yellow",
"price": 12
}
],
"items": [{
"name": "sword",
"price": 20
}],
"specialPowers": [{
"name": "telekinesis",
"price": 34
}]
},
{
"forename": "Peter",
"colors": [{
"name": "blue",
"price": 10
}],
"items": [{
"name": "hat",
"price": 22
},
{
"name": "hammer",
"price": 27
}
]
}
];
console.log(totalPrice(data));
You can use the function reduce and the function includes to select the desired targets.
var inputData = [{ "forename": "Maria", "colors": [{ "name": "blue", "price": 10 }, { "name": "yellow", "price": 12 } ], "items": [{ "name": "sword", "price": 20 }], "specialPowers": [{ "name": "telekinesis", "price": 34 }] }, { "forename": "Peter", "colors": [{ "name": "blue", "price": 10 }], "items": [{ "name": "hat", "price": 22 }, { "name": "hammer", "price": 27 } ] }];
function totalize(possibleArrays, data) {
return data.reduce((a, c) => {
return a + Object.keys(c).reduce((ia, k) => {
if (possibleArrays.includes(k)) c[k].forEach(p => ia += p.price);
return ia;
}, 0);
}, 0);
}
var total = totalize(["colors", "items", "specialPowers"], inputData);
console.log(total);
Something like this should also do it, I just logged the results in console, but you can do pretty much what you want with them :
const getSum = (person, prop) => {
let total = 0;
if(person[prop])
for (let value of person[prop]) {
total = total + value.price;
}
return total;
}
const props = ['colors', 'items', 'specialPowers']
console.log(data.map(person => props.map(prop => getSum(person, prop))));
Edit
I didn't get that you wanted to sum up all your properties for one person at once, this code is what I definitely what I would go for :
const sum = (a, b) => a + b;
const props = ['colors', 'items', 'specialPowers']
data.map(person =>
props.map(prop =>
(person[prop] || [])
.map(({price}) => price)
.reduce(sum, 0)
).reduce(sum, 0)
)
And if you want to sum all person's total price :
data.map(person =>
props.map(prop =>
(person[prop] || [])
.map(({price}) => price)
.reduce(sum, 0)
).reduce(sum, 0)
).reduce(sum, 0)

How to return object based on value in nested array? (Javascript)

I am trying to return all objects that have a specific 'id' in the nested array. In the sample data, I'd like to return all person objects with hobbies id of 2 (hiking).
The other question addresses the problem of finding all values in an array based on an object value.
This question differs from the previous because I need to return all objects based on a value inside of a nested array.
[
{
"id":111222,
"name":"Faye",
"age":27,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":3,
"name":"eating"
}
]
},
{
"id":223456789001,
"name":"Bobby",
"age":35,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":4,
"name":"online gaming"
}
]
}
]
function hasHobby(person, hobbyId) {
return person.hobbies.some(function(hobby) {
return hobby.id === hobbyId;
});
}
function filterByHobby(people, hobbyId) {
return people.filter(function(person) {
return hasHobby(person, hobbyId);
});
}
If you wanna use the new cool ES6 syntax:
function filterByHobby(people, hobbyId) {
return people.filter(
person => person.hobbies.some(
hobby => hobby.id === hobbyId
)
);
}
var arr = [
{
"id":111222,
"name":"Faye",
"age":27,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":3,
"name":"eating"
}
]
},
{
"id":223456789001,
"name":"Bobby",
"age":35,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":4,
"name":"online gaming"
}
]
}
];
arr.filter(function(obj) {
var hobbies = obj.hobbies;
var x = hobbies.filter(function(hob) {
if (hob.id == "2") return true;
});
if (x.length > 0) return true;
});
Try this, I think its solve your proble:
var arr = [{
"id": 111222,
"name": "Faye",
"age": 27,
"hobbies": [{
"id": 2,
"name": "hiking"
}, {
"id": 3,
"name": "eating"
}]
}, {
"id": 223456789001,
"name": "Bobby",
"age": 35,
"hobbies": [{
"id": 2,
"name": "hiking"
}, {
"id": 4,
"name": "online gaming"
}]
}];
var x = arr.filter(function(el) {
var rnel = el.hobbies.filter(function(nel) {
return nel.id == 2;
});
return rnel.length > 0 ? true :false;
});
alert(x.length);

Categories

Resources