1
\$\begingroup\$

How can I further optimize following student average coding challenge?

Statement:- Given a list of N students, every student is marked for M subjects. Each student is denoted by an index value. Their teacher Ms. Margaret must ignore the marks of any 1 subject for every student. For this she decides to ignore the subject which has the lowest class average. Your task is to help her find that subject, calculate the total marks of each student in all the other subjects and then finally return the array of the total marks scored by each student.

Input Specification: input1: An integer value N denoting number of students

input2: An integer value M denoting number of subjects

input3: A 2-D integer array of size N'M containing the marks of all students in each subject.

Output Specification:

Return an integer array of size N containing the total marks of each student after deducting the score for that one subject.

const students = 3 
const subjects = 5
const arr = [
    [75, 76, 65, 87, 87],
    [78, 76, 68, 56, 89],
    [67, 87, 78, 77, 65]
]

let map = arr.reduce((acc, list) => {
    list.forEach((mark, i) => {
        if(!acc[i]) {
            acc[i] = mark
        } else {
            acc[i] = acc[i] + mark
        }
    });

   return acc;
}, {});

let indexToIgnore = 0;
let lowest = map[0] / students

for (const i in map) {
    if(i !== '0') {
        if (Object.hasOwnProperty.call(map, i)) {
            const total = map[i];
            const average = total / students
            if((average) < lowest) {
                indexToIgnore = Number(i)
            }
        }
    }
}


const result = arr.reduce((acc, list) => {
    const sum = list.reduce((_acc, no, i) => i !== indexToIgnore ? _acc + no: _acc ,0)
    acc.push(sum);
    return acc;
}, [])

console.log(result) // => [ 325, 299, 296 ]
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

How can I further optimize student average coding challenge?


For a real-world code review, code should be correct, maintainable, robust, reasonably efficient, and, most important, readable.

For your coding challenge, as you requested, I promoted efficiency to be the most important criterion.


I revised your code by simplifying code, stripping out extraneous code, minimizing loops, and improving the accuracy of the results.

  • Encapsulate the solution in a function.
  • Minimize allocations
  • Simplify by using simple loops.
  • Use the same loops for totals summation and subject summation and low average.
  • The order of an average, sum/n, and a sum is the same, we don't need to calculate sum/n.
  • At the end, we already have the student marks sum, so simply subtract the low average mark for the returned total.

You use JavaScript IEEE 64-bit floating-point numbers and falsely assume that, for all subjects where the average SUM(mark)/students is equal, that SUM(mark/students) will be exactly equal. IEEE floating-point fractional values are an approximation. For an example, see the Floating-Point Approximation Error section at the end of this answer.


Using JSBench.me, your code is about 87% slower than my revised code.

Here is my revised version of your code:

'use strict';

function totalMarks(N, M, students) {
    let totals = Array(N).fill(0);

    let lowIndex = 0, lowValue = Number.MAX_VALUE;
    for (let i = 0; i < M; i++) {
        let subject = 0;

        for (let j = 0; j < N; j++) {
            let mark = students[j][i];
            subject += mark;
            totals[j] += mark; 
        }

        if (lowValue > subject) {
            lowIndex = i, lowValue = subject;
        }
    }

    for (let i = 0; i < N; i++) {
        totals[i]-= students[i][lowIndex];
    }

    return totals;
}

// data
const students = [
    [75, 76, 65, 87, 87],
    [78, 76, 68, 56, 89],
    [67, 87, 78, 77, 65]
];
const N = students.length, M = students[0].length;

// benchmark
totalMarks(N, M, students); 

// test
console.log(students);
console.log(totalMarks(N, M, students)); // => [ 325, 299, 296 ]


Floating-Point Approximation Error

In this example, we sum the all the elements of the same array, sum1 for indexes 0 to 9, sum2, in reverse order, for indexes 9 to 0. The sums, sum1 and sum2, are not exactly equal due to floating-point rounding error.

'use strict';

function floatingpointApproximation() {
    let f = Array(10);
    f[0] = 0.0;
    for (let i = 1; i < f.length; i++) {
        f[i] = 1 / i;
    }
    console.log(f);

    let sum1 = 0;
    for (let i = 0; i < f.length; i++) {
        sum1 += f[i];
    }
    console.log("sum1", sum1);

    let sum2 = 0;
    for (let i = f.length - 1; i >= 0 ; i--) {
        sum2 += f[i];
    }
    console.log("sum2", sum2);

    console.log("sum1 === sum2", sum1 === sum2);
}

floatingpointApproximation();

$ node floaterr.js
f: [
  0,
  1,
  0.5,
  0.3333333333333333,
  0.25,
  0.2,
  0.16666666666666666,
  0.14285714285714285,
  0.125,
  0.1111111111111111
]
sum1: 2.8289682539682537
sum2: 2.828968253968254
sum1 === sum2: false
$ 

Why don’t my numbers add up?

IEEE 754

What Every Computer Scientist Should Know About Floating-Point Arithmetic


\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.