'MaxCounters (lesson 4 in codility) - 100% correctness but 60% on efficiency, why?

Link to the problem in a nutshell: N is an integer that represent number of counters and the max counter allowed. A is an array that represent the operation done on specific counter(for example if A[0] is 1, and N is 3 we need to add 1 to 'counters' array[0]) if element in A is N+1, all element in counter should be change to the largest number in the counter array. I submitted the code I wrote and got only 60% in performance, why is that? any way I should aproach a problem next time to make it more efficient? how can I improve?

function solution(N,A){

let counters = Array(N).fill(0);
let maxCounter = 0;
for(i=0;i<A.length;i++){
    if(A[i]<=N){
        counters[A[i]-1]++
        if(counters[A[i]-1]>maxCounter){maxCounter = counters[A[i]-1]};
    }
    else if(A[i]===N+1){
        counters = Array(N).fill(maxCounter)
    }
}
return counters

}

Edit: I didn't know that this website wasn't meant for question regarding code improvement, thanks, I will ask somewhere else.



Solution 1:[1]

One possible improvement would be, when you need to fill the whole array with the new max counters, don't create an entirely new array to do so - instead, change the existing array.

else if(A[i]===N+1){
    counters.fill(maxCounter)
}

This could have a large effect if there are a whole lot of counters.

Solution 2:[2]

Here is a solution using object which will generate the actual array only once (after all operations are applied).

The "tracker" object will only ever hold the indices & values of those counters which have an operation. Say, N (ie, "num" number of counters) is 50,000 but only 5,000 counters have an explicit operation in A (ie, "arr" array), only those 5,000 elements will be tracked (using the "tracker" object).

Code Snippet

// alternative solution using object
const counterOps = (num, arr) => {
  // the "tracker" object we will use to populate the result array
  const tracker = {};
  
  // when "num+1" to reset all elements to "max", then
  // the "max" value is stored in "lastMaxSet"
  let lastMaxSet = 0;
  
  // helper method to get current max from "tracker"
  const getMax = obj => Math.max(...Object.values(obj));
  
  // helper method to "set" "all" values to "max"
  const setMax = obj => {
    lastMaxSet = getMax(obj);
    Object.keys(obj).forEach(k => obj[k] = lastMaxSet);
  };
  
  // iterate through "arr" and "apply" each "operation"
  arr.forEach(elt => {
    if (elt === num + 1) {
      // "reset to max" operation is applied
      setMax(tracker);
    } else {
      // a particular counter is incremented
      const k = elt - 1;
      tracker[k] ??= lastMaxSet;
      tracker[k]++;
    }
  });
  
  // the "tracker" object is used to generate
  // the result-array on-the-fly
  return [...Array(num).fill(lastMaxSet)].map(
    (val, idx) => idx in tracker ? tracker[idx] : val
  );
};

console.log(counterOps(5, [3, 4, 4, 6, 1, 4, 4]));
.as-console-wrapper { max-height: 100% !important; top: 0 }

Please try out and share feedback if it helped at all (with performance).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CertainPerformance
Solution 2 jsN00b