4

I have two arrays of same length and I would like to somehow process them both at once with the reduce method. Something like:

var arr1 = [2, 3, 4, 5, 6];
var arr2 = [5, 10, 4, 9, 5];
var productSum = arr1.reduce(function(sumOfProducts, item, secondArrayItem) {
    /* here I would like to multiply item from arr1 by the item from arr2 at the same index */
    return sumOfProducts + item * secondArrayItem;  
}, 0, arr2)
console.log(productSum);  // 131

An option would of course be to access the correct item from arr2 using currentIndex, but that solution is ugly as I am accessing a variable outside of the scope of the function.

My specific use case is that I have an array with resources like var resources = [2, 5, 4, 6, 2] and I want to check if each item is higher than corresponding resource cost in another array like var cost = [3, 1, 0, 0, 1].

Is there some nice solution to this using the reduce() function?

BoltKey
  • 1,994
  • 1
  • 14
  • 26
  • 2
    You'd typically do this by zipping the 2 lists together, then reduce the zipped list. Don't know how to idiomatically zip lists in JS though. – Carcigenicate Jun 06 '17 at 21:30
  • 4
    "*that solution is ugly as I am accessing a variable outside of the scope of the function.*" - I don't see what's ugly about that, we do this all the time when accessing globals or using closures. The important thing is just that the variable is constant. What really is ugly about this solution is a) it's not symmetric (accessing arr1 works different than accessing arr2) and b) we didn't ensure that the arrays have the same length, so we don't know whether `arr2[i]` accesses a valid index. – Bergi Jun 06 '17 at 21:42

3 Answers3

2

Using Ramda you can first zip the two lists, and then reduce it and use destructuring to extract the array elements and pass them as arguments to the callback:

const reduceTwo = (callback, initialValue, arr1, arr2) =>
  R.reduce((acc, [x, y]) => callback(acc, x, y), initialValue, R.zip(arr1, arr2));

const arr1 = [2, 3, 4, 5, 6];
const arr2 = [5, 10, 4, 9, 5];
console.log(reduceTwo((acc, x, y) => acc + x * y, 0, arr1, arr2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
1

The standard way that I know to do this is to combine the 2 lists into a list of corresponding pairs ("zipping"), then reduce the combined lists:

var zipped = zip(arr1, arr2)

reduce((acc, [x, y]) => (Use x and y), 
              zipped) 

For implementations of zip, see this question.

(Will verify syntax when I got off of transit)

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Is this the way to go even for huge arrays? I don't want to create a whole new array when I just want to iterate over the values of both arrays at once. – BoltKey Jun 06 '17 at 21:43
  • 4
    @BoltKey If you want to work with huge arrays, you should forget `reduce` and use a simple for loop. – Michał Perłakowski Jun 06 '17 at 21:47
  • 1
    @BoltKey This is a much better approach when dealing with lazy structures, I'll admit. It will require 1 iteration to zip, then another to reduce, and creates an intermediate list, so no, this will not be performant unfortunately. – Carcigenicate Jun 06 '17 at 21:47
  • @BoltKey And I agree with Michal (sorry, I butchered that). By using a simple for-loop that loops using an index, you can index both lists at once easily. That entirely gets rid of the need for any intermediate lists and unnecessary iteration. Sometimes good-old imperative methods are the best. – Carcigenicate Jun 06 '17 at 21:54
0

You may do as follows with pure JS;

var arr1 = [2, 3, 4, 5, 6],
    arr2 = [5, 10, 4, 9, 5],
  result = arr1.reduce((r,c,i) => (r.sum = r.sum ? r.sum + c * r[i] : c * r[i], r), arr2.slice()).sum;
console.log(result);

I .slice() arr2 in order to not mutate it with a state variable yet you may still do without slicing if you will not apply a for in loop to arr2 afterwards or such...

Redu
  • 25,060
  • 6
  • 56
  • 76