0

I can't understant result of use array reduce to find max property

I'm trying to add node to array, while adding I'd like to add id to node. Here is example to test

localNodes: [
    {id:0, x:20, y:20},
    {id:1, x:50, y:50}
]

addNode(node){
  let maxId = localNodes.reduce( (a, b) => Math.max(a.id, b.id) ); 
  maxId++
  //localNodes.push({id:maxId, ...node})
  localNodes.push({id:maxId, x:node.x, y:node.y})
}

addNode({x:20, y:20})
addNode({x:20, y:20})
addNode({x:20, y:20})

Here is strange results:

  • Added nodes in array looks like 'Observer'
  • first added node have correctly calculated id = 2
  • all following nodes have id set to NaN

Output:

localNodes: Array(5)
0: {…}
1: {…}
2: {__ob__: Observer}    -> id = 2
3: {__ob__: Observer}    -> id = NaN
4: {__ob__: Observer}    -> id = NaN

Could you please explain what I did wrong and may be point how to fix it.

AlexeiP
  • 581
  • 1
  • 10
  • 26

1 Answers1

1

First of all let's look at the definition of the reduce function

it takes a function with four arguments

Array​.prototype​.reduce((accumulator, currentValue, currentIndex, array) => {
    // the function must return a value.
    // accumulator: the accumulator accumulates the return value. 
    // currentValue: The current element being processed in the array.
    // currentIndex: The index of the current element being processed in the array. Starts at index 0 if an initialValue is provided and at index 1 otherwise.
    // array: The array reduce() was called upon.  
}, initialValue);

if you don't provide an initial value, the accumulator will be the first element and the currentValue will be the second element in the array


Now the first time you run your function it returns 1 because there's only two values to check

translates to this

// a = accumulator = {id:0, x:20, y:20}
// b = currentValue= {id:1, x:50, y:50}
Math.max(a.id, b.id) 

which is fine, however when there's more this happens

First cycle

// a = accumulator = {id:0, x:20, y:20}
// b = currentValue= {id:1, x:50, y:50}
Math.max(a.id, b.id)  // return 1

Second cycle

// a = accumulator = 1 returned from the first cycle
// b = currentValue= {id:2, x:20, y:20}
Math.max(a.id, b.id) // returns NaN because 1.id doesn't makes sense

Solution

let localNodes = [
    {id:0, x:20, y:20},
    {id:1, x:50, y:50}
]

function addNode(node){
  let maxId = localNodes.reduce( (accumulator, currentValue) => {
    /*
      here we gonna start with 0 which means the accumulator
      will equal 0 at the first cycle and the currentValue
      will be the first element in the array
    */
    Math.max(accumulator,currentValue.id)
  },0); 
  
  maxId++
  localNodes.push({id:maxId, x:node.x, y:node.y})
}

addNode({x:20, y:20})
addNode({x:20, y:20})
addNode({x:20, y:20})

console.log(localNodes)
.as-console-wrapper {
  max-height: 100% !important;
}
Rainbow
  • 6,772
  • 3
  • 11
  • 28
  • Your solution in the reduce function is missing "id" for a.id `Math.max(a,b.id)`. Also worth mentioning that OP's original code had a syntax error with initializing the localNodes array. It should be let `localNodes = [];` not `let localNodes : [];` – Woodrow Apr 24 '19 at 21:00
  • 1
    i don't think that a typo, it's probably a class definition or the OP could be coming from a different environment and forgot to correct the syntax also `Math.max(a,b.id)` is fine i edited the answer with better names so you can understand. – Rainbow Apr 24 '19 at 21:15
  • Ah, gotcha. Thanks for the clarification! – Woodrow Apr 24 '19 at 21:19
  • @Zohir Salak Thank you a lot! – AlexeiP Apr 24 '19 at 23:28