3

Can someone explain to me the differences between these examples please?

Here's the original code.

const items = [
    { name: "Bike", price: 100 },
    { name: "TV", price: 200 },
    { name: "Album", price: 10 },
    { name: "Book", price: 5 },
    { name: "Phone", price: 500 },
    { name: "Computer", price: 1000 },
    { name: "Keyboard", price: 25 },
];

const total = items.reduce((total, amount) => {
    return total + amount.price;
});

console.log("total", total);

above returns total [object Object]200105500100025 <- what is this [object Object] and also why is the result 200105500100025 instead of 1840?

const total = items.reduce((total, amount) => {
    return total + amount.price;
}, 0);

this code returns total 1840 which is what I was looking for but why does having ,0); at the end change this result? Does it change the price values to 'numbers' instead of an 'object' or 'string' like the above?

I noticed that some tutorails on YouTube say that because it's an arrow function I don't need to type 'return' and make the code look like:

const total = items.reduce((total, amount) => {total + amount.price}, 0);

But this returns me a result of total undefined What's the difference between having return and not having it?

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Jason
  • 47
  • 11
  • Reading the documentation for [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) and for [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) should have helped you resolve both of these issues – charlietfl Jul 19 '20 at 02:53

2 Answers2

3
  1. The issue with [object Object] is, as you noticed yourself, the missing , 0.

    This is because you can use .reduce in two ways: With a starting value, and without. Without a starting value, the first call to your callback will have the first two elements as arguments, so the first element is used as starting value and the first call is skipped. This makes sense in some cases such as when you do [1, 2, 3].reduce((a, b) => a + b) where 1 + 2 is a valid first operation, but it doesn't make sense in your case where you actually access a property of each element.

    In your case, that means that the first call will have { name: "Bike", price: 100 } as total and { name: "TV", price: 200 } as amount. You are then doing { name: "Bike", price: 100 } + 200 which will be interpreted as string concatenation and turn the first object into [object Object], resulting in [object Object]200. From then on, the remaining prices are also concatenated (because the intermediate value is now a string), resulting in [object Object]200105500100025 at the end.

    With the , 0, you specify 0 as starting value. In that case, the first call to your callback function will have 0 as total and { name: "Bike", price: 100 } as amount, which makes a lot more sense. You then do 0 + 100, correctly resulting in the number 100, and so on.

  2. The problem with undefined is that when you are writing (total, amount) => { total + amount.price } instead of (total, amount) => total + amount.price you are not returning anything.

    (args) => xyz is a shorthand for (args) => { return xyz }. As soon as you add the { }, you are no longer using the shorthand and hence you have to take care of returning manually. Writing just { xyz } would evaluate xyz but not return anything, as you saw it happening here. This makes more sense if you think about that the main use case is a block with several statements inside the braces, probably spanning multiple lines, and the return statement could then be at any place that you want inside of that block.

CherryDT
  • 25,571
  • 5
  • 49
  • 74
  • 1
    Wow.. I usually don't understand people's answers so I have to read it over and over again to be able to understand but you made it so easy by explaining it in layman's term. Thank you! – Jason Jul 19 '20 at 03:10
1

When you don't include an initial value as the second argument for .reduce(), the accumulator (ie: number) for the reduce callback starts off as the first value in your array, and the element (ie: amount) begins at the second element in your array.

In your case, this means that the number is initially:

{ name: "Bike", price: 100 }

which you then add with amount.price. As you're adding an object with a number, the object's .toString() method is called resulting in [object Object]. This means your accumulator is now a string, so any further additions are really concatenation operations with a string. When you include an initial value though, the accumulator (number) starts off as 0, and the element (amount) starts off as the first object. This means you when you do total + amount.price you're adding a number with a number, resulting in the correct value.

I noticed that some tutorails on YouTube say that because it's an arrow function I don't need to type 'return'

When using arrow functions, you don't have to include a function body (ie: {}). When you don't include the body, the return is implicit (ie: not needed), and it will automatically return the expression to the right of the =>. However, when you have a body, you need to include an explicit return, as the body can include many statements within it. When you don't return from a function body your function will return nothing, which is why you're getting undefined. An example of it working without the body would be to use:

const total = items.reduce((total, amount) => total + amount.price, 0);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64