High order functions...reduce()

Hi all -
I was able to resolve the exercise in Use Higher-Order Functions map, filter, or reduce to Solve a Complex Problem using filter() and map(). I decided to try using reduce (option B in my code), but I’m not sure what I’m doing wrong. Additionally, is it really necessary to use reduce? I thought reduce() takes various elements and reduces them to a single item. Thus not sure, if it’s appropriate to use it. Anyhow, the exercise indicated we could use it, but I’m not sure what i’m doing wrong. Any guidance is really appreciated.
Code using reduce()

const squareList = arr => {
 var positiveNumbers = [];
 var newArr = arr.reduce((squareNum, num) => {
   Number.isInteger(num) && num > 0 ? squareNum = num * num: null;
 }, 0);
   **Full code--- using filter and map**

const squareList = arr => {
 //OPTION A
 
 var newArr = arr.filter(num => Number.isInteger(num)? num: null); 
 var noNegative = [];
 var positiveNum = newArr.map(num => num > 0 ? noNegative.push(num) : null);
 var squaredNum = noNegative.map(num => num * num);
 return squaredNum;
//END OF OPTION A
  
//OPTION B with REDUCE
/* 
const squareList = arr => {
 var positiveNumbers = [];
 var newArr = arr.reduce((squareNum, num) => {
   Number.isInteger(num) && num > 0 ? squareNum = num * num: null;
 }, 0);
 console.log(newArr);
 return newArr;
 // Only change code above this line
};
*/

}

const squaredIntegers = squareList([4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2]);
console.log(squaredIntegers);
   **Your browser information:**

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36

Challenge: Use Higher-Order Functions map, filter, or reduce to Solve a Complex Problem

Link to the challenge:

When you’re using a .reduce() the result is one ‘thing’, but that ‘thing’ could be an array. This means that you can rewrite a .filter() and a .map() as a .reduce().

For example,

const myMappedArr = myArr
  .reduce((acc, elem) => {
    acc.push(2 * elem);
    return acc;
  },
  []);

this code will do the same thing as

const myMappedArr = myArr
  .map(elem => 2 * elem);

(Side note: You should not use var. Use only const or let.)

In your code, you have

but, since you start with a number, 0, you cannot end up with an array.

Also, your callback function does not have a return value as you’ve written it, which is means your accumulator will become undefined.

4 Likes

Jeremy’s got you covered on reduce, but I wanted to touch on some of your code that’s using filter and map, because it looks like your understanding is a little shaky in some places.

arr.filter(num => Number.isInteger(num)? num: null);

For this line, you’ve got the right idea to use filter. The goal here being to take the arr array and remove a subset of items from it, in this case non-integers. So the Number.isInteger method is a great pick! But I wonder about the ternary expression ? num : null. Do you mind going in to your thought process there?

var noNegative = [];
var positiveNum = newArr.map(num => num > 0 ? noNegative.push(num) : null);

These lines are breaking some cardinal rules so I need to touch on those. Most of all the goal for this line is to filter out the numbers in the array that are not positive. Are you sure that map is the best choice to use in this situation? Second, the use of push here is a big red flag. For the callback function to map, all you need to do is return the new value you want to transform the old value into. So in the case of multiplying by two it would be something like [1, 2, 3].map(x => x * 2). This would return a new array that contains [2, 4, 6], but you don’t need to create that array yourself, nor do you need to push values anywhere. map handles that for you.

var squaredNum = noNegative.map(num => num * num);

This is beautiful (minus the var). Use map like this every time!

3 Likes

Good catch! As a rule of thumb, may, filter, and reduce should never have side effects. This means that they should not change anything outside of the scope of the callback function. If you want a loop, do a forEach.

Hi JeremyLT- Thank you so much for this. First of all, i didn’t think of an array as a “one thing,” so thank you for clarifying that. Your code for myMappedArr using reduce and map make so much sense. However, I do have one question about your code using reduce.

const myMappedArr = myArr
  .reduce((acc, elem) => {
    acc.push(2 * elem);
    return acc;
  },
  []);

Why are you using [brackets] before closing the parenthesis? I understand you are supposed to use (parenthesis) when dealing with objects, do we have to add [brackets] when dealing with arrays? I thought a zero (0) was needed instead?
As for var, let, and cost, thanks for the advice. I have to do some reading on the differences, but would you mind explaining briefly. I can get more using const for this exercise, but not sure i fully comprehend let. Is it recommended to not use var and use let and const instead?

In regards to your last sentence,

Also, your callback function does not have a return value as you’ve written it, which is means your accumulator will become `undefined` .

thank you so much. I thought everything that came after => already meant a ‘return’ was implied. Is that not always the case?

Again, thank you soooooooo much for this. I really appreciate it!

I really like using MDN as a starting place for my research, but it takes some getting used to. I’ll point out where in MDN I figured out what’s going on.

In this case, this part of the syntax is what is important:

reduce(function callbackFn(previousValue, currentValue, currentIndex, array) { ... }, initialValue)

When you are using .reduce() you can supply an initial value. In this case, we’re making an array, so our initial value should be an empty array.

If no initial value is provided, then the very first value in our array is used instead. But the very first value in our array is not an array, so it will not produce the results we want.

const myMappedArr = myArr
  .reduce(
    // This is the callback function
    (acc, elem) => {
      acc.push(2 * elem);
      return acc;
    },
    // This is the initial value
    []
  );
1 Like

Hi Colin- Thank you so much for your help. I appreciate you all taking the time to help me understand things better.

But I wonder about the ternary expression ? num : null. Do you mind going in to your thought process there?

Absolutely, i initially used a standard "if clause " without using an “else statement,” but i was trying to make my code 'shorter, ’ so I thought i could use a ternary operator. The only thing is that you need 2 options when using the ternary operator, and since i only wanted to use if, then i thought “null” was necessary as the else statement so that nothing would run. Does that make sense?

As for breaking some cardinal rules, thank you for flagging it.

var noNegative = [];
var positiveNum = newArr.map(num => num > 0 ? noNegative.push(num) : null);

After you have pointed it out, i see that using filter here makes more sense than using map(). I do have to ask you though about your comment regarding .push(). Why do you mean, it’s a red flag? Would you mind elaborating? Do you mean it’s not ok in this particular instance as map basically does that? Or are you talking in a broader sense? I want to make sure I’m using things properly.

Lastly, your last comment:

var squaredNum = noNegative.map(num => num * num);
This is beautiful (minus the var). Use map like this every time!

when you say minus var, you mean to use ‘let’ or ‘const’ instead, right? Thanks much!

Again, thank you so much for taking the time to explain this. Much appreaciated.

In short, map filter and reduce should never be used as forEach or for loops. You should only change the output of the map/filter/reduce (postitiveNum in this case) and never any additional variables (such as noNegative in this case).

1 Like

You are the BEST!!! Thank you so much!!!

When you are using .reduce() you can supply an initial value. In this case, we’re making an array, so our initial value should be an empty array.

This is great. I believe having glimpsed this at some point, but it didn’t click until now with the example. Thank you so very much!!!

1 Like

I see. This is great. thanks much! I really appreciate the help!

1 Like

@colinthornton - disregard my last sentence. I re-read it, and i know you said no need to create a variable for map.

On the ternary operators thing, going to the docs for the callback function to filter, there’s this:

Return a value that coerces to true to keep the element, or to false otherwise.

In other words, there’s only one of two things that should happen

  1. The filter callback returns a truthy value, so the item is added to the new array
  2. The filter callback returns a falsy value, so the item is not added to the new array

Now, to be fair, your callback does return truthy and falsy values, since the numbers coerce to true and null coerces to false.

x => Number.isInteger(x) ? x : null

x returns truthiness
1 1 truthy
2 2 truthy
2.2 null falsy

Buuuut think about the return value from Number.isInteger for a moment. When the input is an integer, it returns true. When the input is not an integer, it returns false. Here’s the chart for that.

x => Number.isInteger(x)

x returns
1 true
2 true
2.2 false

You can see that after the coercions on the ternary operator version, the results are the same as just using Number.isInteger as is.

That was a very roundabout way to say this:

Instead of

arr.filter(num => Number.isInteger(num) ? num: null);

Why not just this?

arr.filter(num => Number.isInteger(num))

The result is the same, and in my opinion it’s easier to comprehend at a glance. (You could even drop the arrow function and just pass Number.isInteger, but I won’t go into that)


For the push, as Jeremy said, you shouldn’t create side effects in the callbacks to filter, map, and reduce. Said another way, you should strive to make those functions as pure as possible.


Right, now that let and const exist, there’s pretty much no reason to use var under any normal circumstances. My rule of thumb is to always default to const, then if you find later that you need to be able to mutate the variable, change it to let. In your original code you could replace all of the instances of var with const.

2 Likes

Thank you so much for explaining this in detail. I really appreciate it. It makes it easier for me to understand it. Thank you ! thank you!!

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.