This is my first post, so I will try to do my best to be as thorough as possible! I need to convert multiple JavaScript objects that have a same value into a single unique object with the totals of all other keys in the object from each.
I have an array that contains objects for different schools, and each has a value for the number of students at the school. Here is what I am working with:
let schools = [
{
school: "a",
students: 1000,
},
{
school: "b",
students: 10000,
},
{
school: "b",
students: 400,
},
{
school: "c",
students: 10000,
},
{
school: "d",
students: 2000,
},
{
school: "c",
students: 1000,
},
{
school: "d",
students: 4000,
},
]
I want to return an array of single objects for each given school, with the sum of values of students in each of the given schools' objects. Here is the result I am expecting:
{
school: "a",
students: 1000,
},
{
school: "b",
students: 10400,
},
{
school: "c",
students: 11000,
},
{
school: "d",
students: 6000,
}
]
Here is what my solution looks like so far:
let listOfUniqueSchools = [];
for (let i = 0; i < schools.length; i ) {
if (!listOfUniqueSchools.includes(schools[i].school)) {
listOfUniqueSchools.push(schools[i].school);
} else {
}
}
let outputArray = [];
for (let j = 0; j < listOfUniqueSchools.length; j ) {
for (let k = 0; k < schools.length; k ) {
let totalNumberOfStudents = 0;
if (listOfUniqueSchools[j] == schools[k].school) {
totalNumberOfStudents = schools[k].students;
outputArray.push({
school: schools[k].school,
students: totalNumberOfStudents,
});
}
}
}
Here is what I am getting:
[
{
school: "a",
students: 1000,
},
{
school: "b",
students: 10000,
},
{
school: "b",
students: 400,
},
{
school: "c",
students: 10000,
},
{
school: "c",
students: 1000,
},
{
school: "d",
students: 2000,
},
{
school: "d",
students: 4000,
},
]
Does anyone know what I need to change to solve this?
CodePudding user response:
This solution uses a for loop (forEach) to populate a new object that has schools as keys and student totals as the values. Then we use the Object.keys method to get an array of schools that are found in the totals object, and we use the map function to transform the data into the expected output format.
It's a lot cleaner than nested loops.
const schools = [{
school: "a",
students: 1000,
},
{
school: "b",
students: 10000,
},
{
school: "b",
students: 400,
},
{
school: "c",
students: 10000,
},
{
school: "d",
students: 2000,
},
{
school: "c",
students: 1000,
},
{
school: "d",
students: 4000,
},
]
const totals = {}
schools.forEach((entry) => {
if (totals.hasOwnProperty(entry.school)) {
totals[entry.school] = entry.students
} else {
totals[entry.school] = entry.students
}
})
const formattedTotals = Object.keys(totals).map((school) => ({
school: school,
students: totals[school]
}))
console.log(formattedTotals)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
The problem in your code is the Array.push
part is in the inner loop while it should be at outer loop.
outputArray.push({
school: listOfUniqueSchools[j],
students: totalNumberOfStudents,
});
Move it outside it will work.
It could be simpler using Array.reduce
, see the code snippet below.
let schools = [ { school: "a", students: 1000, }, { school: "b", students: 10000, },{ school: "b", students: 400, }, { school: "c", students: 10000, }, { school: "d", students: 2000, }, { school: "c", students: 1000, }, { school: "d", students: 4000, },];
let listOfUniqueSchools = [];
for (let i = 0; i < schools.length; i ) {
if (!listOfUniqueSchools.includes(schools[i].school)) {
listOfUniqueSchools.push(schools[i].school);
} else {
}
}
let outputArray = [];
for (let j = 0; j < listOfUniqueSchools.length; j ) {
let totalNumberOfStudents = 0;
for (let k = 0; k < schools.length; k ) {
if (listOfUniqueSchools[j] == schools[k].school) {
totalNumberOfStudents = schools[k].students;
}
}
outputArray.push({
school: listOfUniqueSchools[j],
students: totalNumberOfStudents,
});
}
console.log(outputArray);
// or simply use array reduce
const result = schools.reduce((acc, item) => {
const sum = acc.find(sum => sum.school === item.school);
sum ? sum.students = item.students : acc.push({school: item.school, students: item.students});
return acc;
}, []);
console.log(result);
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
I would loop through the schools and create an object where keys are schools ("a", "b", "c", etc.) and values are the cumulative number of students.
Then cycle through that array to create an array of objects, each of which has a key "school" and value, and a key "students" with the cumulative value.
let schools = [
{
school: "a",
students: 1000,
},
{
school: "b",
students: 10000,
},
{
school: "b",
students: 400,
},
{
school: "c",
students: 10000,
},
{
school: "d",
students: 2000,
},
{
school: "c",
students: 1000,
},
{
school: "d",
students: 4000,
},
]
let totals = {} // Interim totals
schools.forEach( function(school) {
// console.log(school.students, school.school)
totals[school.school] = ( totals[school.school] ?? 0 ) school.students;
})
let results = [] // Final results
for ( const school in totals ) {
results.push(
{
school: school,
students: totals[school]
}
)
}
console.log(results)
/*
Array(4) [ {…}, {…}, {…}, {…} ]
0: Object { school: "a", students: 1000 }
1: Object { school: "b", students: 10400 }
2: Object { school: "c", students: 11000 }
3: Object { school: "d", students: 6000 }
length: 4
*/