0

I have a two dimensional Array in Javascript and want to select each value seperately in a random order.

(Basically this

for(var i = 0; i < example.length; i++) {
 for(var j = 0; j < example.length; j++) {
  doSomething(example[i][j]);
 }
}

but in a (seemingly) random order)

Is there an efficient way of achieving this or will I have to keep track of what i have already selected/still have to select?

  • Depending on how many items you have in each array, you could shuffle each array before iterating (do it once for the outer one, and then before each inner loop) and then iterate – GammaGames Oct 24 '19 at 19:40
  • 1
    I'm guessing you'll have to make the array 1D. You can then shuffle it maybe, and just access the items – Ahmad Sattout Oct 24 '19 at 19:40
  • 1
    Generally the advice is shuffle the array, then draw items out of it. In your case, shuffle the inner arrays, then shuffle the outer array, then just draw them out. See [javascript: shuffle 2D array](https://stackoverflow.com/q/35556898/215552) – Heretic Monkey Oct 24 '19 at 19:40
  • 1
    Or you could shuffle your array... (See: https://stackoverflow.com/questions/52241641/shuffling-multidimensional-array-in-js) – Screeper Oct 24 '19 at 19:40

3 Answers3

0

Traversing in random order is usually best done by shuffling the array and then iterating the shuffled array in order. This has the advantage of having worst-case runtime O(n), as opposed to randomly selecting indices which is O(infinity).

Given your array is 2D, you should flatten it before shuffling, because shuffling the inner arrays and outer array individually will not be uniformly random. E.g., you'll never iterate [[1, 2], [3, 4]] in the order [1, 3, 2, 4] unless you flatten before shuffling.

Implementation is simple:

let arr = [
  [1, 2],
  [3, 4]
];
let shuffled = arr.flat();
shuffled.forEach((v, i, a) => {
  let j = Math.floor(Math.random() * (a.length - i)) + i;
  shuffled[i] = shuffled[j];
  shuffled[j] = v;
});
shuffled.forEach(v => console.log(v));
junvar
  • 9,037
  • 1
  • 19
  • 35
0

You could flatten the Arrays (get one Array containing all the values). In modern JS, there is Array.prototype.flat() which allows you to do this, but it's pretty recent, so not working in IE and Edge. But you can use libraries such as Lodash, or can make a polyfill for it (see example below).

Then, all you need to do is shuffle it. Again, you can use a library or use your own method:

// Original post from Gumbo https://stackoverflow.com/a/10865042/1913729
function flatten(arr) { return [].concat.apply([], arr); }

// Original post: https://stackoverflow.com/a/6274381/1913729
function shuffle(a) {
  var j, x, i;
  for (i = a.length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    x = a[i];
    a[i] = a[j];
    a[j] = x;
  }
  return a;
}

var example = [['a', 'b', 'c'], [1, 2, 3]];
var shuffledValues = shuffle(flatten(example));

shuffledValues.forEach(function (v) { console.log(v); });
blex
  • 22,377
  • 5
  • 35
  • 65
0
// thanks to https://stackoverflow.com/a/6274381/12153266
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}

const indexes = shuffle([...Array(example.length).keys()]);
indexes.forEach(i => {
    const subIndexes = shuffle([...Array(example[i].length).keys()]);
    subIndexes.forEach(j => doSomething(example[i][j])); 
}

This way you iterate over the shuffled indexes of example, and then for each example[i] you iterate over its own shuffled indexes.

Ewaren
  • 783
  • 6
  • 13
  • You should give credit when using other answers (https://stackoverflow.com/a/6274381/6951428). – junvar Oct 24 '19 at 19:49
  • This is also not random, i.e., you'll never iterate `[[1, 2], [3, 4]]` in the order of `[1, 3, 2, 4]` – junvar Oct 24 '19 at 19:52
  • I'm not sure if this is a constraint on his side though, especially since he precised "seemingly random" – Ewaren Oct 24 '19 at 19:56