Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How does one pick the col/row or x/y coordinates of N random elements of a 2D matrix without item/coordinate duplicates?

I have a 2D matrix, here’s an example data = [["a", "b", "c", "d"], ["e", "g"], ["i", "j", "k"]]

I need to get N random (x, y) indexes without duplicates.

I already asked a different question same context and this is the solution to pick 2 x, y combos

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

const data = [["a", "b", "c", "d"], ["e", "g"], ["i", "j", "k"]];

function combinations(data) {
  const i11 = Math.floor(Math.random() * data.length);
  const i12 = Math.floor(Math.random() * data[i11].length);

  const dataLength = data[i11].length > 1 ? data.length : data.length - 1;

  let i21 = Math.floor(Math.random() * dataLength);
  if (i21 >= i11 && data[i11].length === 1) ++i21;

  const innerDataLength = i21 === i11 ? data[i21].length - 1 : data[i21].length;
  let i22 = Math.floor(Math.random() * innerDataLength);
  if (i21 === i11 && i22 >= i12) ++i22;
  
  return [[i11, i12], [i21, i22]];
}

console.log(combinations(data));

for (let i = 0; i < 10000; ++i) {
    const [[i11, i12], [i21, i22]] = combinations(data);
    if (i11 === i21 && i12 == i22) console.log('Test failed!');
}

>Solution :

function pickNOf(list, n) {
  // - create a new shallow array copy.
  // - does decouple the original reference, thus it
  //   prevents its further mutation by e.g. `splice`.
  list = Array.from(list);

  // - creates and returns an array of the desired length
  //   by a mapping task which constantly slices random
  //   items from the shallow array copy.
  return Array.from({ length: n }, () => list.splice(
    // - `splice` does mutate the list by removing a
    //   single item from its randomly chosen index.
    Math.floor(Math.random() * list.length), 1
  )[0]);
}

function pickNUniqueItemsFromNestedArray(arr, n) {
  // - flatten nested array structure,
  // - channel it through a `Set` in order
  //   to keep just unique values,
  // - return the result of the forwarded
  //   `pickNOf` call.
  return pickNOf([...new Set(
    arr.flat(Infinity)
  )], n);
}

const data = [
  ["a", "b", "c", "d"],
  ["e", "g"],
  ["i", "j", "k"]
];
console.log(
  "pickNUniqueItemsFromNestedArray(data, 9) ...",
  pickNUniqueItemsFromNestedArray(data, 9)
);
console.log(
  "pickNUniqueItemsFromNestedArray(data, 5) ...",
  pickNUniqueItemsFromNestedArray(data, 5)
);
console.log(
  "pickNUniqueItemsFromNestedArray(data, 3) ...",
  pickNUniqueItemsFromNestedArray(data, 3)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Edit due to …

Thank you it works, but i need indexes –
Amine

But that’s not what the question’s topic doe state … "How to pick N random elements from a 2D matrix without duplicates in javascript?" Nevertheless I will provide a second solution which utilizes the first given approach in order to fully meet the OP’s requirements. –
Peter Seliger

In order to meet the OP’s requirements the above given approach changes slightly to …

function pickNOf(list, n) {
  // - create a new shallow array copy.
  // - does decouple the original reference, thus it
  //   prevents its further mutation by e.g. `splice`.
  list = Array.from(list);

  // - creates and returns an array of the desired length
  //   by a mapping task which constantly slices random
  //   items from the shallow array copy.
  return Array.from({ length: n }, () => list.splice(
    // - `splice` does mutate the list by removing a
    //   single item from its randomly chosen index.
    Math.floor(Math.random() * list.length), 1
  )[0]);
}

function pickNUniqueIndexTuplesFrom2dMatrix(matrix, n) {
  // - create a flat array of any of a
  //   matrix' index coordinates/tuples.
  return pickNOf(
    matrix.flatMap((arr, rowIdx) =>
      arr.map((_, colIdx) => [rowIdx, colIdx])
    ), n
  );
}

const data = [
  ["a", "b", "c", "d"],
  ["e", "g"],
  ["i", "j", "k"]
];
console.log(
  "pickNUniqueIndexTuplesFrom2dMatrix(data, 9) ...",
  pickNUniqueIndexTuplesFrom2dMatrix(data, 9)
);
console.log(
  "pickNUniqueIndexTuplesFrom2dMatrix(data, 5) ...",
  pickNUniqueIndexTuplesFrom2dMatrix(data, 5)
);
console.log(
  "pickNUniqueIndexTuplesFrom2dMatrix(data, 3) ...",
  pickNUniqueIndexTuplesFrom2dMatrix(data, 3)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Note

Despite or maybe even because of the misunderstanding it might become more clear that the base approach itself does not change. Which is … pick n random items from an array without duplicates … and …

  • Split the main task into more specialized sub tasks. (Like demonstrated with the implementation of pickNOf which solves a single problem perfectly, thus it does not need to be refactored.)

  • The other task/s has/have to transform the input data in a way that it can be passed to the specialized task of picking n random items from an array without duplicates (which hopefully got proofed by the two different approaches each targeting a different requirement).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading