9

I need to call function with all avaliable pairs of array elements. Like this:

[1, 2, 3].pairs(function (pair) {
  console.log(pair); //[1,2], [1,3], [2,3]
});
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
user3447503
  • 121
  • 1
  • 1
  • 5

7 Answers7

13

You should try to show us that you've solved the problem yourself instead of just asking us for the answer, but it was an interesting problem, so here:

Array.prototype.pairs = function (func) {
    for (var i = 0; i < this.length - 1; i++) {
        for (var j = i; j < this.length - 1; j++) {
            func([this[i], this[j+1]]);
        }
    }
}

var list = [1, 2, 3];
list.pairs(function(pair){
    console.log(pair); // [1,2], [1,3], [2,3]
});

http://jsfiddle.net/J3wT5/

altumano
  • 2,676
  • 2
  • 26
  • 20
MattDiamant
  • 8,561
  • 4
  • 37
  • 46
11

Thanks to the continuous evolvement of the ECMAScript standard ..

let pairs = (arr) => arr.map( (v, i) => arr.slice(i + 1).map(w => [v, w]) ).flat();

pairs([1, 2, 3, 4, 5]);
antonjs
  • 14,060
  • 14
  • 65
  • 91
5
function pairs(arr) {
    var res = [],
        l = arr.length;
    for(var i=0; i<l; ++i)
        for(var j=i+1; j<l; ++j)
            res.push([arr[i], arr[j]]);
    return res;
}
pairs([1, 2, 3]).forEach(function(pair){
    console.log(pair);
});
Oriol
  • 274,082
  • 63
  • 437
  • 513
3

Here is a variant in ES6 style without mutations:

const pairsOfArray = array => (
  array.reduce((acc, val, i1) => [
    ...acc,
    ...new Array(array.length - 1 - i1).fill(0)
      .map((v, i2) => ([array[i1], array[i1 + 1 + i2]]))
  ], [])
) 

const pairs = pairsOfArray(['a', 'b', 'c', 'd', 'e'])
console.log(pairs)

// => [['a','b'], ['a','c'], ['a','d'],['a','e'],['b','c'],['b','d'],['b','e'],['c','d'],['c','e'],['d','e']]
musemind
  • 1,027
  • 9
  • 8
3

With flatMap now available in ECMAScript, there's a really simple and readable option to find pairs of items in a list in Javascript with no loops:

const pairs = (a) => {
    
    return a.flatMap( (x) => {
        return a.flatMap( (y) => {
            return (x != y) ? [[x,y]] : []
        });
    });
}

calling pairs([1,2,3]) will output:

[ [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 3 ], [ 3, 1 ], [ 3, 2 ] ]

Simple, readable and functional.

EDIT: I originally read this question as "how to get all pairs" which I assumed included reversed pairs, which the above example does. To return the list without the reversed order pairs, we can take these out with reduce():

const isInArray = (a, value) => {
    if (
        a.map((x) => {
            if (x.toString() == value.toString()) return true;
        }).includes(true)
    ){
        return true;
    }
};

const reducedPairs = (a) => {
    
    return a.flatMap( (x) => {
        return a.flatMap( (y) => {
            return (x != y) ? [[x,y]] : []
        });
    }).reduce( (unique, current) => {
        if (!isInArray(unique, current.slice().reverse())) unique.push(current);
        return unique;
    }, []);

}

Note that this uses string comparisons to check for array equality since [1,2] == [1,2] is false. This works for the use case in the original question, but for a more complex example an alternative method of checking for duplicates may be required.

Harry
  • 4,660
  • 7
  • 37
  • 65
1

Here is the cleanest solution that I could find using ES6. It generates unique pairs based on the index and using the flatMap function.

const uniquePairs = (arr) => 
  arr.flatMap((item1, index1) => 
    arr.flatMap((item2, index2) => 
      (index1 > index2) ? [[item1,item2]] : []
    )
  )

uniquePairs([1,2,3])
// [[2, 1], [3, 1], [3, 2]]

flatMap - returns elements into one array, and has the benefit of being able to remove empty arrays entirely. This is what allows our ternary with a value of [] to just disappear!

Like map, it can return the index, and by comparing the index of both items, we can avoid duplicates (the 1st index must be greater than the 2nd for us to add an element).

The ternary lets us do a comparison and returns an empty array if false (which get's completely ignored by the 2nd flatMap).

By changing each flatMap into a map you can have a better idea of what each flatMap is accomplishing- so I encourage you to try altering it!

Ethan Jurman
  • 111
  • 1
  • 6
  • Your answer is nice, but is clearly a duplicate of Harry's one: https://stackoverflow.com/a/65064026/3933603 – F3L1X79 Dec 03 '21 at 15:17
  • 1
    While Harry's is good, it does not prevent duplicate unique lists. Instead it provides every pairing including duplicates. It then goes to use reduce (as an extension) to provide unique items- which is verbose since you can do it simply by comparing the index values (which is something that Harry does not do in his solution). I know on the surface they look similar but you should compare the outputs of his original solution and mine before saying they are "clearly a duplicate". – Ethan Jurman Jan 01 '22 at 21:30
  • You are right, I am sorry for the rude comment. Thank your for the detailled explanation and good job here! – F3L1X79 Jan 03 '22 at 08:18
0

In case you're looking for reversed pairs as well - going off of @Oriol's answer above - here's a version that includes all original pairs + all original pairs reversed:

function allPairs (arr) {
  let pairs = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      pairs.push([arr[i], arr[j]]);
    }
  }
  let copy = pairs;
  pairs = JSON.stringify(pairs);
  let reversed = [];
  copy.forEach((value) => {
    reversed.push(value.reverse());
  })
  let final = [];
  final.push(JSON.parse(pairs).concat(reversed));
  return final[0];
}
console.log(allPairs([1, 2, 3, 4, 5]));
HappyHands31
  • 4,001
  • 17
  • 59
  • 109