How does reduce() work here?

Could someone explain what exactly does reduce() do here? What are (a, b) referring to? If I delete the method, it just returns the entire collection, so why does attaching reduce to it returns the correct result?

function whatIsInAName(collection, source) {
  const souceKeys = Object.keys(source);
  return collection.filter(obj => souceKeys
      .map(key => obj.hasOwnProperty(key) && obj[key] === source[key])).reduce((a, b) => a && b)
}

console.log(whatIsInAName([{ "apple": 1, "bat": 2 }, { "apple": 1 }, { "apple": 1, "bat": 2, "cookie": 2 }], { "apple": 1, "cookie": 2 }))

The first parameter a stores the accumulator, or the result you are eventually assumed to return by reducing the array. The second parameter b is the current element we execute the callback we pass to reduce. The callback is supposed to run for each element, while keeping a result(inside a) for the mapping. What the callback returns in the end, is what a is going to be assigned for the next “iteration”(if we imagine we are looping throught the array). In this case, the callback returns a && b. Here it is used as short-circuit operator, meaning if the first part of the expression is true, we are going to return the second, else we return the first. Practically, we are going to run throught the array elements, compare them with one another and wheenver the preceding one is true, the next one is going to be passed for the next iteration, else the preceding is going to be kept. I hope that made sense. Here is more info on &&

Here’s how I solved that

function getRating(watchList) {
  let ratings = watchList.filter(a => a.Director === 'Christopher Nolan').map(a => Number(a.imdbRating))
  let avgRating = ratings.reduce((a, b) => a + b) / ratings.length
  return avgRating
}

console.log(getRating(watchList));

But I’m still not sure what a and b are referring to in the first code

So is a and b referring to the conditions obj.hasOwnProperty(key) and obj[key] === source[key] ?

I tried to build a function following the same model. It should return [3, 4]. What am I doing wrong?

let x = [3, 4]
let y = [[1], [2], [3], [4], [5]]
let z = y.filter(arr => x.map(n => arr.includes(n) && arr[0] > 2)).reduce((a, b) => a && b)
console.log(z)

I’m trying to understand one of the solutions to this challenge https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/wherefore-art-thou

function whatIsInAName(collection, source) {
  const souceKeys = Object.keys(source);
  console.log(souceKeys)
  return collection.filter(obj => souceKeys
      .map(key => obj.hasOwnProperty(key) && obj[key] === source[key])).reduce((a, b) => a)
}

So I tried creating a similar code, using the same logic as far as I understood it. But it’s not working.

let x = [3, 4]
let y = [[1, 3, 4], [2, 4, 3], [3, 4, 1], [4, 3, 2], [5, 3, 4]]
let z = y.filter(arr => x.map(n => arr.includes(n) && arr[0] + arr[1] === 7)).reduce((a, b) => a && b)
console.log(z)

It’s supposed to return [[3, 4, 1], [4, 3, 2]] but it always returns the last array. I tried replacing filter with map in both codes to see what was going on, and true and false values were appearing as expected according to the conditional statements. Then why does my code not reduce it to [[true, true], [true, true]] which would result in the correct output?

Are you sure you want to include all of this in the map callback? Don’t you just want to check if each number in array x is in arr?

Actually, I take this back a little, this isn’t doing what you think it is doing. If I understand correctly, you only want to include subarrays in y that include both of the values in x? The map method isn’t what you want here. I think you are looking for the every method, which will run a test on every value in x and only return true if every value in x is included in arr.

If the correct answer should be [[3, 4, 1], [4, 3, 2]] then what is the purpose of this reduce?

No, that’s not what I’m trying to do. There’s a second condition that only 2 arrays satisfy. And I know how to solve this using every(), I came up with this code just to replicate the initial one that I posted to try to understand it, but it’s not working. Why does reducing work in the initial code and not in mine?

I guess I’m not exactly sure what you are trying to accomplish here?

First, do you know what array filter is creating? If not, then comment out the reduce to see what the console.log prints out. Next, do you know what the && operator does when there are arrays on both sides of it? This should be pretty easy to test. If you understand that and you understand how reduce works then I think it should be clear why it’s returning the last subarray in y.

I’ve done all of that. What I don’t understand is why does reduce() in the initial code return the array that has all true values in it, and my code just returns the last one regardless of values inside?

Both reduces only return a single value. The reduced result is used to filter in the initial code you posted.

Why does it not work in the same manner in my code? Everything is almost identical, the only difference is that I have arrays instead of objects.

The reduce works the same way in your code. The reduce only returns a single value in both cases.

In the original code, the result from the reduce is used as a condition for a filter. In really it’s an awkward approach. If that is a posted solution, it should be updated.

Yes, it is a posted solution. Can you please explain why in my code it is not used as a condition for filter? I’m just trying to understand the mechanism here.
" * Then we reduce the mapped Boolean values to a single Boolean that indicates whether all srcKeys pass the conditions checked above."
This is from the explanation. Isn’t that what my code is doing as well?

Look at your ()s

This is why I like to format dot functions on their own line

1 Like

I don’t see it, the () placement is identical to the solution code, isn’t it?

What do you want to reduce? The results of the filter? Or the results of the map? Right now you are reducing the results of the filter.

I don’t even know how it works so I’ve no idea what I’m doing, I just want to understand how the reduce() works in the solution code, so I built a similar one to analyze it in hopes to understand it.

  // filter the collection
  return collection.filter(obj => souceKeys
      .map(key => obj.hasOwnProperty(key) && obj[key] === source[key])
      .reduce((a, b) => a && b));

Different () s in the solution

It’s really a convoluted version of

  // filter the collection
  return collection.filter(
    obj => souceKeys
      .every(key => obj.hasOwnProperty(key) && obj[key] === source[key]
  );

1 Like

Yes I completely understand the solution using every(), but I would really like to understand the other one. Where is the difference in the ()s?

let z = y.filter(arr => x
           .map(n => arr.includes(n) && arr[0] + arr[1] === 7))
           .reduce((a, b) => a && b);