I need to delete nested for loop in a function I created. My function receive an associative array and I return a new array based on certain properties in order to group messages to use later. For example, I have two schools, many students. So I group them based on gender and grade. I don't how to refactor this function because I don't know much about algorithms. It doesn't matter if my logic need be erased completely or need to be done again. I must delete the second for loop. Also, I can return either an common array, associative array or just object. I tried to replicate my function with the same logic but different data:
var studentsArray = new Array();
studentsArray["SCHOOL_1"] = [
// girls
{id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: false},
{id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!', isMan: false},
{id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok', isMan: false},
// boys
{id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true},
{id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},
{id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},
{id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true},
{id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},
];
studentsArray["SCHOOL_2"] = [
// girls
{id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},
{id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},
{id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},
{id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!', isMan: false},
{id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!', isMan: false},
// boys
{id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},
{id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},
{id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},
{id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!', isMan: true},
];
function GroupMessages(schools, gender) {
// Initialize object to return
var result = [];
// First loop
for (var school in schools) {
// Group students by gender
var girls = schools[school].filter(student => !student.isMan);
var boys = schools[school].filter(student => student.isMan);
// Flag to determine unique grade per gender
var boysHaveUniqueGrade = boys.map(student => student.grade).filter((v, i, a) => a.indexOf(v) === i).length === 1;
var girlsHaveUniqueGrade = girls.map(student => student.grade).filter((v, i, a) => a.indexOf(v) === i).length === 1;
// If exists a single student per gender, return the same
if (girls && girls.length === 1) result.push(girls[0]);
if (boys && boys.length === 1) result.push(boys[0]);
//////////////////////////
// Group by grades //
/////////////////////////
if (boys && boys.length > 1 && boysHaveUniqueGrade && gender === 'man') {
// Combine messages
let messages = boys.map(boy => boy.message);
// First student is the reference
let student = boys[0];
// Join messages
student.message = messages.join('|');
// Update object to return
result.push(student);
}
if (boys && boys.length > 1 && !boysHaveUniqueGrade && gender === 'man') {
// Group messages by level (maybe I don't need GroupByProperty function neither)
let studentsByGrade = GroupByProperty(boys, 'grade');
// Second loop. I return a boys students based on 'grade' property. (I NEED TO DELETE THIS SECOND FOR LOOP)
for (let grade in studentsByGrade) {
// First student is the reference
let student = studentsByGrade[grade][0];
// Combine messages
let messages = studentsByGrade[grade].map(student => student.message);
// Join messages
student.message = messages.join('|');
// Update object to return
result.push(student);
// Code continue but I stop code here...
}
}
if (girls && girls.length > 1 && girlsHaveUniqueGrade && gender !== 'man') {
// Combine messages
let messages = girls.map(girl => girl.message);
// First student is the reference
let student = girls[0];
// Join messages
student.message = messages.join('|');
// Update object to return
result.push(student);
}
if (girls && girls.length > 1 && !girlsHaveUniqueGrade && gender !== 'man') {
// Group messages by level (maybe I don't need GroupByProperty function neither)
let studentsByGrade = GroupByProperty(girls, 'grade');
// Second loop. I return a girls students based on 'grade' property. (I NEED TO DELETE THIS SECOND FOR LOOP)
for (let grade in studentsByGrade) {
// First student is the reference
let student = studentsByGrade[grade][0];
// Combine messages
let messages = studentsByGrade[grade].map(student => student.message);
// Join messages
student.message = messages.join('|');
// Update object to return
result.push(student);
// Code continue but I stop code here...
}
}
}
return result;
}
function GroupByProperty(objectArray, property) {
let result = objectArray.reduce((acc, obj) => {
var key = obj[property];
if (!acc[key]) acc[key] = [];
acc[key].push(obj);
return acc;
}, {});
return result;
}
GroupMessages(studentsArray, 'woman'); // any other gender works as 'man'
new Array.code with another logic. it would also be helpful to see the expected output.