loading...

How to use the `reduce` Method in JavaScript (and React)

yogesnsamy profile image yogesnsamy ・2 min read

What is reduce

It's an ES5 method in JavaScript like for..each and map which we can use with arrays.

The difference is:

  • as the name suggests it reduces the number of values in an array to one.
  • we have access to the array's previous value apart from its current value and index.
  • we will send the accumulator's start value to the callback function; so when we first start previous value will be the same as the accumulator's start value

A Simple Example of Using reduce

Let's look at a simple example that uses reduce to count the total of an array.
Imagine you have an array: [98,45,33,47,100,80]
We can write the following code to use the reduce method to sum up the values in this array:

const scores = [98,45,33,47,100,80];
const totalScores = scores.reduce(
(previousScore, currentScore, index)=>previousScore+currentScore, 
0);
console.log(totalScores); //returns 403

What happens in the code is:

  1. we call the reduce method on the array scores.
  2. the method has access to the array's previous value, current value and index. *We don't use index in this example.
  3. we send zero as the accumulator's initial value.
  4. in our example when the method first runs (where currentScore is 98), previousScore assumes the value of zero as sent to the callback function.
  5. the result of totalScores is 403.
  6. if we change the initial value of the accumulator to 100, the value of totalScores then changes to 503.
const scores = [98,45,33,47,100,80];
const totalScores = scores.reduce(
(previousScore, currentScore, index)=>previousScore+currentScore, 
100);
console.log(totalScores); //returns 503

Using reduce in React

Imagine you have the following data structure in the App component:

  const course = {
    name: 'Half Stack application development',
    parts: [
      {
        name: 'Fundamentals of React',
        exercises: 10,
        id: 1
      },
      {
        name: 'Using props to pass data',
        exercises: 7,
        id: 2
      },
      {
        name: 'State of a component',
        exercises: 14,
        id: 3
      }
    ]
  }

To display the total number of exercises in in the Total component:

  1. from App send parts as a prop to Total:
  2. in Total, call the reduce method on parts.
  3. parts contains multiple values name, exercises and id.
  4. so we explicitly specify exercises as the value we want to use in the calculation.
  • App.js:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Total from "./components/Total";

const App = () => {
  const course = {
    name: "Half Stack application development",
    parts: [
      {
        name: "Fundamentals of React",
        exercises: 10,
        id: 1
      },
      {
        name: "Using props to pass data",
        exercises: 7,
        id: 2
      },
      {
        name: "State of a component",
        exercises: 14,
        id: 3
      }
    ]
  };

  return (
    <div>
      <Total parts={course.parts} />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
  • Total.js:
import React from "react";

const Total = props => {
  const total = props.parts.reduce(
    (prevValue, currentValue) => prevValue + currentValue.exercises,
    0
  );
  return <p>Totalzzz: {total}</p>;
};

export default Total;
  • Result: Alt Text

Hope this helps!

Discussion

pic
Editor guide
Collapse
mindplay profile image
Rasmus Schultz

Okay, that's the how.

The bigger question for me has always been why?

A plain for-loop is almost always both faster and more readable. For one, you can name your total variable, which helps people understand what your code is trying to do.

Even in situations where I'm forced to use an expression (e.g. JSX) I prefer adding an extra function.

The only arguments I've heard in favor of reduce is things like "feels more functional", which just never really outweighs priorities like simplicity and speed for me.

If there's a case where a reduce call doesn't easily refactor into a simpler, faster, more readable procedural version of the same thing, I haven't seen it yet. πŸ€·β€β™‚οΈ

Collapse
yogesnsamy profile image
yogesnsamy Author

Yeah I've wondered about the why too and I finally learnt about reduce thanks to a React exercise that required its use. :)

But now that I've tried it, I do find the code shorter and simpler to write though I agree it's not very readable.

With the for-loop, my code is much longer like the following. Any feedback/suggestion on this?

 const totalz2 = () => {
    let total2 = 0;
    for (let i = 0; i < props.parts.length; i++) {
      total2 += props.parts[i].exercises;
    }
    return total2;
  };
Collapse
mindplay profile image
Rasmus Schultz

I'd use for-of rather than for-in, since you don't care about the index - introducing it as a variable means the reader has to figure out what's important and filter that out first.

I would also pass props.parts to the function, to illustrate the fact that this function doesn't care about anything else - seeing the function signature, a person understands right away what the dependencies of that function are; sometimes that can answer their question without making them scan through the function body to figure out what input the function requires.

In general, I'd rather have twice as many lines and have something everyone can immediately read and understand (and modify without breaking it!) without a tutorial - and let a minifier do the minifying.

On the issue of inlining vs functions, I lean heavily towards functions, because glazing over a large render-function, I'd rather see descriptive names like countTotalExercises as opposed to an tangle of inline expressions - the reader then knows right away what you're doing and doesn't need to be burdened with how you're doing it, before they need to.

And likely this was just an example, but I'd use more descriptive names. πŸ˜‰

Thread Thread
yogesnsamy profile image
yogesnsamy Author

Thank you so much for taking the time to share your valuable feedback. I like how you prioritize readability over other features. Keeping this in mind moving forward.

Thread Thread
nadabk profile image
nad-abk

@yogesnsamy sorry but no, i prefer by far your way of writing tutorials !! it's so refreshing actually !! and it's straight to the point and step by step thing is ... perfect !!! and the fact that i might have to wonder what every line of code does actually helps a lot !! definitely going to follow you and read every tutorial you'll write !

Thread Thread
yogesnsamy profile image
yogesnsamy Author

You're too kind. Thank you for making my day!

Collapse
ygorbunkov profile image
ygorbunkov

To me, none of the listed differences is true: the outcome of reduce() doesn't have to be a single value - it can be an object, an array of greater/smaller size, or nearly anything; you can access any array item from within other high order methods (like map() or forEach()) at any step, using it's entire set of arguments; you can drag accumulator value in thisArg of above-mentioned methods just as easy.

Collapse
yogesnsamy profile image
yogesnsamy Author

Thank you for your feedback. Quoting MDN, the documentation does say it returns a single value.

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

I'm pretty new to the method, may I know more about your last reference to thisArg please?

Collapse
timhlm profile image
moth

Single value doesn't mean it can't be a collection type I think is the OP's point. For example you can use reduce to easily make a lookup map out of an array:

[
  { id: 123, name: "Ben" },
  { id: 456, name: "Sarah" },
  { id: 789, name: "Jane" }
].reduce((acc, e) => {
  return {
    [e.id]: e.name,
    ...acc
  }
}, {})

/*
output-
{
  123: Ben,
  456: Sarah,
  789: Jane
}
*/
Thread Thread
yogesnsamy profile image
yogesnsamy Author

Yes, thank you for a nice example.

Collapse
brasileiro profile image
Daniel Brasileiro

True. Thanks for sharing.
βž• Still a very good article. πŸŽ‰πŸŽ‰πŸŽ‰Congrats to the author.

Collapse
yogesnsamy profile image
yogesnsamy Author

Thank you for your kind words. :)

Collapse
matias2018 profile image
matias2018

It does help :)
Thank you

Collapse
yogesnsamy profile image
yogesnsamy Author

Glad to hear! :)

Collapse
fidoogle profile image
Fidel Guajardo

I enjoyed your easy to follow writing style and content. I also appreciate your follow ups to the comments. It shows your readers that you are interested in their comments. Good luck to you!

Collapse
yogesnsamy profile image
yogesnsamy Author

Appreciate your kind words. Have a good day. :)

Collapse
iliutastoica profile image
Iliuta Stoica

There is a bug when calling props.parts,
You have to call props.exercises

Collapse
yogesnsamy profile image
yogesnsamy Author

Could you see what you're sending as prop from App?

I'm sending it as the following and just tested that it works:
<Total parts={course.parts} />