10

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

8 Answers 8

14

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/

Sign up to request clarification or add additional context in comments.

1 Comment

I'm not so good specialist, sorry. Googling didn't help;) Thank you so much.
14

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

Comments

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

Comments

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

Comments

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.

Comments

2

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!

3 Comments

Your answer is nice, but is clearly a duplicate of Harry's one: stackoverflow.com/a/65064026/3933603
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".
You are right, I am sorry for the rude comment. Thank your for the detailled explanation and good job here!
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]));

Comments

0

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

    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.