How to convert a list of numbers to unique pairs of two

Viewed 407

I have a situation where I need to convert a random array of whole numbers of an unknown length, to multiple pairs of 2.

Ex:

var numbers = [1, 5, 0, 7, 5, 5, 1, 7, 5, 1, 2, 1];

-----------------------------------------------------
[[1, 5], [0, 7], [5, 5], [1, 7], [5, 1], [2, 1]]

There are a few constraints that need to follow, such as:

  • There should be exactly 6 pairs (each of 2 numbers).
  • If there aren't enough numbers in the numbers array to make 6 pairs, then generate a number that is between 1-9.
  • Each item in the numbers array can be used only once.
  • Each pair must be unique. But e.g [1,2] and [2,1] can be considered different pairs.
  • A pair cannot contain [0, 0]

So far, I found a function that can find combinations but it does not ignore an index once it has already used it.

var numbers = [1, 5, 0, 7, 5, 5, 1, 7, 5, 1, 2, 1];

function p(t, i) {
  if (t.length === 2) {
    result.push(t);
    return;
  }
  if (i + 1 > numbers.length) {
    return;
  }

  p(t.concat(numbers[i]), i + 1);
  p(t, i + 1);
}

var result = [];
p([], 0);
console.log(result);

What should I do next?

3 Answers

You could take some loops, one for checking the lenght of the result set, one for checking if there a values available of array and add values for taking the next pair and check if the pair has seen before.

If not add the pair to the result set and continue.

var getRandomN = n => () => Math.floor(Math.random() * n) + 1,
    getRandom = getRandomN(9),
    array = [1, 5, 0, 7],
    seen = new Set(['0|0']),
    length = 6,                        // specify wanted length
    result = [],
    i = 0,
    pair;

while (result.length < length) {       // check result with wanted length
    do {
        while (array.length < i + 2) { // check if a pair is available
            array.push(getRandom());   // if not add new random value
        }
        pair = array.slice(i, i += 2); // get a pair and increment index
    } while (seen.has(pair.join('|'))) // repeat if a pair has seen before

    seen.add(pair.join('|'));          // add a fresh pair to the set
    result.push(pair);                 // take the pair for result
}

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Although Nina may be right about OP needing only the first N pairs from an Array of unknown length, here's an extra check for it (and a slightly different approach to the problem):

const nums1 = [1, 5, 0, 7, 5, 5, 1, 7, 1, 5, 1, 5];
const nums2 = [1, 5, 0, 7, 5, 5, 1, 7, 5, 1, 2, 1];
const nums3 = [1, 5, 0, 7, 5, 5, 0, 0, 5, 1, 2, 1, 0, 0, 1, 8];
const nums4 = [1, 5, 0, 7, 1, 6, 5, 5, 0, 0, 5, 8, 8, 5, 5, 1, 2, 1, 0, 0, 1, 8];
const log = pairs => console.log(JSON.stringify(pairs));
log(createPairs(nums1, 6));
log(createPairs(nums2, 6));
log(createPairs(nums3, 6));
log(createPairs(nums4, 5));
log(createPairs(nums4, 5, true));

function createPairs(numbers, len, truncate) {
  let pairs = [];
  let i = 0;
  const existingValues = new Set(['0|0']);
  const randomDigit = n => Math.floor(Math.random() * (n + 1));

  // create and complete array of [len] unique pairs, not being [0, 0]
  while (pairs.length < len) {
    const pair = i + 2 <= numbers.length
      ? numbers.slice(i, i += 2) 
      : [randomDigit(9), randomDigit(9)];
    const pairStr = pair.join(`|`);
    !existingValues.has(pairStr) && pairs.push(pair);
    existingValues.add(pairStr);
  }
  
  return i + 2 < numbers.length && !truncate 
    ? `This would deliver more pairs than desired (${len})` 
    : pairs;
}
.as-console-wrapper {
  top: 0;
  max-height: 100% !important;
}

var numbers = [1, 5, 0, 7, 5, 5, 1, 7, 5, 1, 2, 1];
var arr = [];
numbers.forEach(function (x, i) {
  if(i % 2) {
    arr[arr.length-1][1] = x;
    console.log(arr[arr.length-1]);
  }
  else {
    arr.push([x, null]);
  }
});
console.log(arr.join("#"));
Related