6

I have an array of numbers [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123], I need to use reduce to create an object that looks like this:

{  
   numbersLessThanTen: [...],  
   numbersGreaterThanTen: [...]
}

I have the solution, which is the below:

const listOfNumbers = [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123];


const groupedBySize = listOfNumbers.reduce((total, next) => {
  const less = total.numbersLessThanTen || [];
  const more = total.numbersGreaterThanTen || [];
  
  next > 10 ? total.numbersGreaterThanTen = [].concat(more, next) : total.numbersLessThanTen = [].concat(less, next);
  return total;
  
}, {});

My question is, why does the following not work? It just returns the initial value. It works when I use .push() instead of .concat() but I really want to understand why this way does not work. Thank you!

const groupedBySize = listOfNumbers.reduce((total, next) => {
  // const less = total.numbersLessThanTen || [];
  // const more = total.numbersGreaterThanTen || [];
  
  next > 10 ? total.numbersGreaterThanTen.concat(next) : total.numbersLessThanTen.concat(next);
  return total;

}, {numbersGreaterThanTen: [], numbersLessThanTen:[]});

1
  • Thank you all for your help! They were all informative answers, I just found the way Akrion's solution read was more suitable for me personally Commented Dec 3, 2018 at 3:25

4 Answers 4

1

The reason your code doesn't work is becase concat() returns the result of concatentation as a new array. When your reduce function runs, the concatentation work is done, but the result of that concatentation isn't assigned to anything (ie a field in total). This is the reason the output result appears is identical to the input - the arrays in total never actually get updated. The following is from MDN

The concat method creates a new array consisting of the elements in the object on which it is called.

The push() method on the other hand actually mutates/updates the array that it's called on, adding what ever data you pass directly to that array instance. This means when push() is called per iteration, the arrays in total are updated directly which is why this approach works:

The push method appends values to an array.

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

Comments

1

Others explained why that approach didn't work, and gave reasonable answers, continuing the mutation.

Here is an approach that doesn't mutate any data:

const listOfNumbers = [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123];

const groupedBySize = nbr => nbr.reduce 
  ( ({ gt10, lte10 }, next) => next > 10 
     ? { gt10: gt10 .concat(next), lte10 } 
     : { gt10, lte10: lte10 .concat(next) }
  , { gt10: [], lte10: [] }
  )

console.log(groupedBySize(listOfNumbers))

I also shorted the property names. Obviously you could re-lengthen them. But do note that "numbersLessThanTen" is a bit misleading, as it includes 10.

Comments

1

You can decide which array with the ternary and then push() instead of concat():

let n = [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123]

let obj = n.reduce((obj, n) => {
    (n < 10 
     ? obj.numbersLessThanTen
     : obj.numbersGreaterThanTen
    ).push(n)
    return obj
}, {numbersLessThanTen: [],  numbersGreaterThanTen:[]})

console.log(obj)

1 Comment

I was scrolling down to add this exact answer. Too late.
0

Array.concat does not mutate the array on which it is called. You are assuming that and not assigning the result to anything ... as per docs:

This method does not change the existing arrays, but instead returns a new array

So to get what you want with your code you could do something like this:

const listOfNumbers = [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123];

const groupedBySize = listOfNumbers.reduce((total, next) => {  
  let key = next > 10 ? 'numbersGreaterThanTen' : 'numbersLessThanTen'
  total[key] = total[key].concat([next]); // <-- assign the result
  return total;
}, {numbersGreaterThanTen: [], numbersLessThanTen: []});

console.log(groupedBySize)

Alternatively you could just do this:

const listOfNumbers = [22, 44, 12, 9, 4, 23, 1, 11, 10, 5, 2, 123];

const groupedBySize = listOfNumbers.reduce((ttl, nxt) => 
 (ttl[nxt > 10 ? 'numbersGreaterThanTen' : 'numbersLessThanTen'].push(nxt), ttl), 
 {numbersGreaterThanTen: [], numbersLessThanTen:[]});

console.log(groupedBySize)

Which does the same thing but uses Array.push which mutates the array on which it is called by inserting the item.

1 Comment

Thanks for the alternative solutions, I understood that concat does not mutate the array but I see now my problem was assuming that reduce accumulated the two arrays for me and that I would have access to the new concated arrays in the next iteration. I like the way your solution reads better than mine!

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.