0

Is it possible to reduce the number of for loops to just one in this case? I need to group an object based on 'grade' property, no matter the data type of the output, even doesn't matter if your solution force me to start over again. I don't know much about algorithms for now so I need your help to improve my code because I am not allowed to use more than one for loop in a single block of code.

var schools = {
    'SCHOOL_1':
    [
        // girls
        {id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!'},
        {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok'},
        // boys
        {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},
        {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},
        {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},

    ],
    'SCHOOL_2':
    [
        // girls
        {id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!'},
        {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!'},
        // boys
        {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!'},
    ]
}

var result = [];

for (let school in schools) {
    // Group students by grade
    let studentsByGrade = GroupByProperty(schools[school], 'grade');
    // Second loop (I NEED TE REFACTOR THIS)
    for (let grade in studentsByGrade) {
        if (!result[grade]) result[grade] = [];
        result[grade].push(studentsByGrade[grade]);
    }
}

console.log('result',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;
}

3
  • Object.values(schools).flat() Commented Nov 20, 2020 at 13:31
  • 1
    @Keith: beat me to it by seconds. Commented Nov 20, 2020 at 13:32
  • 1
    @ScottSauyet Yeah, but your answer gets the cookie,.. Upvote..! Commented Nov 20, 2020 at 13:33

2 Answers 2

3

By combining Object.values and Array.prototype.flat, you can turn this object into a single list of objects.

This would do it:

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

const byGrades = (schools) => 
  GroupByProperty (Object .values (schools) .flat(), 'grade')

var schools = {'SCHOOL_1': [{id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'}, {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!'}, {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok'}, {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'}, {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'}, {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'}, {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'}, {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'}], 'SCHOOL_2': [{id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!'}, {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!'}, {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'}, {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!'}]}

console .log (byGrades (schools))

Sign up to request clarification or add additional context in comments.

Comments

2

Compact solution using Object.values() flat() and reduce() implemented with destructuring and making use of the nullish coalescing operator.

const bygrade = Object.values(schools)
   .flat()
   .reduce((acc, o) => 
      (acc[o.grade] = [...acc[o.grade] ?? [], {...o}], acc), {});

var schools = {
    'SCHOOL_1':
    [
        // girls
        {id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!'},
        {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok'},
        // boys
        {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},
        {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},
        {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!'},
        {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!'},

    ],
    'SCHOOL_2':
    [
        // girls
        {id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!'},
        {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!'},
        // boys
        {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!'},
        {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!'},
    ]
}

const bygrade = Object.values(schools).flat().reduce((acc, o) => (acc[o.grade] = [...acc[o.grade] ?? [], {...o}], acc), {});

console.log(bygrade);

1 Comment

Yes, although the OP already had a more generic equivalent of your reduce call in the question (GroupByProperty).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.