There are many ways to shuffle an array, but I'll use yours in this answer:
const shuffle = xs => xs.sort(() => Math.round(Math.random()) - 0.5));
Your question contains 3 arrays of similar length. The items in the array are "related" to each other by index:
question.a[i] ~ question.imga[i] ~ question.auda[i]
One way to make sure these relations are respected when shuffling, is to shuffle "trios" of items rather than the individual arrays.
shuffle([
[question.a[0], question.imga[0], question.auda[0]],
/* ... */
])
This leaves two challenges:
1. Create one array of related items from several arrays
2. After sorting, extract the individual arrays from the shuffled trios
1. Zipping
Let's define a zip function:
const zip = (xs, ...others) =>
xs.map(
(x, i) => [x].concat(others.map(ys => ys[i]))
);
console.log(zip([1, 2, 3], ["a", "b", "c"], ["I", "II", "III"]));
2. Extracting
Now, to get back to the three separate arrays, we create a "question constructor" that takes an array of [a, imga, auda]
and converts it to an object with named properties:
const Question = ([a, imga, auda]) => ({ a, imga, auda })
or, more generic:
const zipped = [[1, "a", "I"], [2, "b", "II"], [3, "c", "III"]];
const unzip = xss => xss.reduce(
(t1, t2) => t1.map((x, i) => [].concat(x).concat(t2[i]))
);
console.log(unzip(zipped));
Putting them together:
const q = {
question: "Are my answers shuffled?",
answers: [ "Yes", "No", "Maybe" ],
audio: [ "yeeeeaaah", "naaaa", "ehhhh" ],
img: [ "✔", "⛔️", "❓" ]
};
// Utils
const zip = (xs, ...others) =>
xs.map(
(x, i) => [x].concat(others.map(ys => ys[i]))
);
const unzip = xss => xss.reduce(
(t1, t2) => t1.map((x, i) => [].concat(x).concat(t2[i]))
);
const shuffle = xs => xs.sort(() => Math.random() - 0.5)
// Question shuffler implementation
const shuffleQuestion = q => {
const [answers, audio, img] =
unzip(shuffle(zip(q.answers, q.audio, q.img)));
return Object.assign(
{}, q, { answers, audio, img }
);
};
// Three versions of the question that *might* have
// differently ordered answers
console.log(shuffleQuestion(q));
console.log(shuffleQuestion(q));
console.log(shuffleQuestion(q));
Of course, you can also create a javascript implementation of the approach presented by Matthew:
const reorder = order => xs => order.map(i => xs[i]);
const range = length => Array.from({ length }, (_, i) => i);
const shuffle = xs => xs.sort(() => Math.random() - 0.5);
const myShuffle = reorder(shuffle(range(3)));
console.log(myShuffle([1, 2, 3]));
console.log(myShuffle(["a", "b", "c"]));
console.log(myShuffle(["I", "II", "III"]));