1

Array comprehensions are a feature of Firefox (Gecko) that is not standard, and the Mozilla documentation itself recommends usage of map and filter instead.

Can you convert the following array comprehension to a combination of map and filter?

var numbers = [ 1, 2, 3, 4, 5 ];
var letters = [ "a", "b", "c", "d", "e" ];

var cross = [for (i of numbers) 
             for (j of letters) 
             if (i % 2 == 0) i+j];

Expected result:

[ '2a', '2b', '2c', '2d', '2e', '4a', '4b', '4c', '4d', '4e' ]
Eleno
  • 2,864
  • 3
  • 33
  • 39

2 Answers2

1

What you need to do is first obtain the Cartesian product then you can filter and map the resulting array.

Python

from itertools import product

numbers = [ 1, 2, 3, 4, 5 ]
letters = [ 'a', 'b', 'c', 'd', 'e' ]

cross = map(lambda x:'{0}{1}'.format(*x),filter(lambda e:e[0]%2==0,product(numbers,letters)))

print cross # ['2a', '2b', '2c', '2d', '2e', '4a', '4b', '4c', '4d', '4e']

JavaScript

// Source: http://stackoverflow.com/a/15310051/1762224
function cartesian() {
  var r = [], args = arguments, max = args.length - 1;
  function helper(arr, i) {
    for (var j = 0, l = args[i].length; j < l; j++) {
      var a = arr.slice(0).concat(args[i][j]); // Clone arr
      if (i === max) { r.push(a); }
      else { helper(a, i + 1); }
    }
  }
  helper([], 0);
  return r;
}

var numbers = [ 1, 2, 3, 4, 5 ];
var letters = [ 'a', 'b', 'c', 'd', 'e' ];

var cross = cartesian(numbers, letters)
          . filter(function(item, index, arr) { return item[0] % 2 === 0; })
          . map(function(item, index, arr)    { return item.join(''); });

console.log(cross); // [ '2a', '2b', '2c', '2d', '2e', '4a', '4b', '4c', '4d', '4e' ]

More Efficient Design

As Andy suggested in the comments, you could filter the numbers array ahead-of-time so that you do not need to create such a large product.

var cross = cartesian(
        numbers.filter(function(number) {
            return number % 2 === 0;
        }),
        letters)
    .map(function(item) {
        return item.join('');
    });
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • You are onto something, but the code should use a `permutation` function, not a `zip` one. I have added the expected result to my snippet. – Eleno Feb 11 '16 at 11:32
  • @Elena: I was confused, until you included desired output. I updated my answer. I hope it is the intended result. – Mr. Polywhirl Feb 11 '16 at 11:51
  • Why not just filter out the even numbers and iterate over those? Less painless – Andy Feb 11 '16 at 12:21
  • @Andy: You could do that, but what I provided was a *general* Cartesian product function. Either way, I provided an example of what you requested. Of course you could tailor your own custom function to handle two arrays, but a general *Cartesian Product* function would be desirable;helpful to most people. – Mr. Polywhirl Feb 11 '16 at 12:44
0

This answer also uses reduce to flatten the output:

function comp(numbers, letter) {

  // filter out the even numbers
  return numbers.filter(function (number) {
    return number % 2 === 0;
  }).map(function (number) {

    // for each letter append the number that we're currently
    // iterating (from map)
    return letters.map(function (letter) {
      return number + letter;
    });

  // use reduce and concat to flatten the output of two nested
  // arrays into one array
  }).reduce(function (p, c) {
    return p.concat(c);
  });
}

comp(numbers, letters); // [ "2a", "2b", "2c", "2d", "2e", "4a", "4b", "4c", "4d", "4e" ]

DEMO

Andy
  • 61,948
  • 13
  • 68
  • 95