user3447503
user3447503

Reputation: 131

How to get all pairs of array JavaScript

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]
});

Upvotes: 10

Views: 24928

Answers (8)

MattDiamant
MattDiamant

Reputation: 8771

Interesting problem, here is one way to do it:

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/

Upvotes: 14

NotoriousPyro
NotoriousPyro

Reputation: 647

The top answer is great, but I had to modify it to be more inline with .map:

Array.prototype.pairs = function (callback) {
    if (this.length < 2) {
        return [];
    }
    const arrSize = this.length * (this.length - 1) / 2
    const newArray = new Array(arrSize);
    let index = 0;
    for (var i = 0; i < this.length - 1; i++) {
        for (var j = i; j < this.length - 1; j++) {
            newArray[index] = callback([this[i], this[j+1]], index, newArray);
            index++;
        }
    }
    return newArray;
}

Calling it:

var list = [1, 2, 3];
const pairs = list.pairs(function(pair, index, array){
    console.log(`pair: ${pair} index: ${index} array: ${array}`);
    return pair;
});
console.log(pairs)

Result:

pair: 1,2 index: 0 array: ,,
pair: 1,3 index: 1 array: 1,2,,
pair: 2,3 index: 2 array: 1,2,1,3,
0: (2) [1, 2]
1: (2) [1, 3]
2: (2) [2, 3]

And for typescript you can silence the interpreter by defining the type:

declare global {
    interface Array<T> {
        pairs<U>(callbackfn: (value: T[], index: number, array: T[][]) => U): U[];

    }
}

Upvotes: 0

Ethan Jurman
Ethan Jurman

Reputation: 151

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!

Upvotes: 2

Harry
Harry

Reputation: 4835

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.

Upvotes: 3

antonjs
antonjs

Reputation: 14318

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]);

Upvotes: 14

HappyHands31
HappyHands31

Reputation: 4101

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]));

Upvotes: 0

musemind
musemind

Reputation: 1055

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']]

Upvotes: 3

Oriol
Oriol

Reputation: 288470

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);
});

Upvotes: 5

Related Questions