0

I need to randomize only some elements of an array.

Array example:

let array = [ // index 0 { randomize: true, value: 'value_1' }, // index 1 { randomize: false, value: 'value_2' }, // index 2 { randomize: false, value: 'value_3' }, // index 3 { randomize: true, value: 'value_4' }, // index 4 { randomize: false, value: 'value_5' }, ] 

The array is dynamically created, so it can have a lot of elements with randomize: true on any position. I want to randomize only elements with randomize: true, and others should remain in their current positions. What is the best solution to do it?

EDIT:

Answers from @georg, @Nina Scholz, @TKoL and @Neveen Atik all works. I implemented @georg solution since it is the most elegant.

9
  • 1
    Shuffling an array is usually done by selecting two elements randomly and swapping them (multiple times). Instead of picking any two elements, pick two where .randomize === true Commented Apr 16, 2021 at 11:43
  • The array is dynamically created, so it can have a lot of elements with randomize: true and I don't know their positions. I will add that information in question. Commented Apr 16, 2021 at 11:45
  • 2
    That doesn't matter at all; you pick a random index using Math.floor(Math.random() * array.length) until the picked element's randomize is true. Then you pick a second index using the same method. Then you swap the two elements. It doesn't matter how the array is created or what the positions are. Commented Apr 16, 2021 at 11:46
  • I added index comments, so we know what to talk about Commented Apr 16, 2021 at 11:47
  • @NenadMilosavljevic You mean, you want to have 01234 to be randomly shuffled to e.g 21430 or 01432 or 41032 with index 1 and 3 (as denoted by the randomize value) should stay in place? Commented Apr 16, 2021 at 11:49

4 Answers 4

2

I'd do it like this:

  • extract indexes of random elements
  • shuffle these indexes
  • map the array, and, for a random element, return an element from the shuffled index:

function rnd(a) { let indexes = a .map((x, n) => x.randomize ? n : -1) .filter(x => x >= 0) .map(x => [Math.random(), x]) .sort() .map(x => x[1]); return a.map(x => x.randomize ? a[indexes.pop()] : x); } let arr = [ { randomize: true, value: 'a'}, { randomize: false, value: '2'}, { randomize: true, value: 'b'}, { randomize: false, value: '4'}, { randomize: true, value: 'c'}, { randomize: false, value: '6'}, { randomize: true, value: 'd'}, { randomize: false, value: '8'}, { randomize: false, value: '9'}, { randomize: true, value: 'e'}, ] console.log(...rnd(arr).map(x => x.value)) console.log(...rnd(arr).map(x => x.value)) console.log(...rnd(arr).map(x => x.value)) console.log(...rnd(arr).map(x => x.value)) console.log(...rnd(arr).map(x => x.value)) console.log(...rnd(arr).map(x => x.value))

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

1 Comment

Thank you for your answer. Not only that it's working, but it's elegant too.
2

I think one idea is to make a new array with only the randomizable things, shuffle it, and then place them back into the old array. Check this out:

var array = [ { randomize: true, value: 'value_1' }, { randomize: false, value: 'value_2' }, { randomize: false, value: 'value_3' }, { randomize: true, value: 'value_4' }, { randomize: false, value: 'value_5' }, { randomize: true, value: 'value_6' }, { randomize: true, value: 'value_7' }, ]; // https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array function shuffle(array) { var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } // make a list of all randomizable elements var randomizable = array.filter(o => o.randomize); // shuffle their order shuffle(randomizable); // to make this easy to reason about, i think i'll make all the randomizeable elements in the first array null array.forEach(function(el, index) { if (el.randomize) array[index] = null; }); console.log(array); // now, we can iterate over the null slots and place in elements from randomizable array.forEach(function(el, index) { if (el === null) { array[index] = randomizable.pop(); } }); console.log(array);

5 Comments

I guess you could do if (el.randomize) array[index] = randomizable.pop(); instead of setting the index to null and skip the second loop
@NickParsons I did think that, the only reason i did it this way was because the intermediate step is easier to visualize -- if you set a breakpoint before i start putting the shuffled elements back in, you can clearly see which elements of the array are available to have something put into them.
But you're right, it's not a necessary step at all, and is only useful for the purpose I stated.
sure no worries. Yeah, it's a good way to visualize how your code is working :)
Thank you for your answer. I tested it and it works. I implemented @georg's answer though, since it’s a little more elegant.
1

You could get the indices shuffle them and apply the random values back to a new array.

const array = [{ randomize: true, value: 'value_1' }, { randomize: false, value: 'value_2' }, { randomize: false, value: 'value_3' }, { randomize: true, value: 'value_4' }, { randomize: false, value: 'value_5' }], indices = [...array.keys()] .filter(i => array[i].randomize) .map((v, i, a) => { const j = (Math.random() * (a.length - i) | 0) + i, t = a[j]; a[j] = v; return t; }), result = array.map((o, i, a) => o.randomize ? a[indices.shift()]: o); console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

1 Comment

Thank you for your answer. I tested it and it works. I implemented @georg's answer though, since it’s a little more elegant.
1

You can do something like

// inspired by this answer https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array function shuffle(array) { let currentIndex = array.length; let randomIndex = Math.floor(Math.random() * currentIndex); // While there remain elements to shuffle... while (0 !== currentIndex) { let currentElement = array[currentIndex -1]; //if currentElement is randomizable if(currentElement.randomize){ // Pick another element that is randomizable while(!array[randomIndex].randomize){ randomIndex = Math.floor(Math.random() * currentIndex); } currentIndex -= 1; // And swap it with the current element. array[currentIndex] = array[randomIndex]; array[randomIndex] = currentElement; // otherwise go to previous element } else { currentIndex -= 1; } } return array; } // Used like so const array = [ // index 0 { randomize: true, value: 'value_1' }, // index 1 { randomize: false, value: 'value_2' }, // index 2 { randomize: false, value: 'value_3' }, // index 3 { randomize: true, value: 'value_4' }, // index 4 { randomize: false, value: 'value_5' }, ] shuffle(array); console.log(array);

2 Comments

Thank you for your answer. I tested it and it works. I implemented @georg's answer though, since it’s a little more elegant.
I agree my function can use some grooming :) However I was more concerned with performance since @georg's answer is like 6n and this would be 2n if I'm not mistaken. Happy you found your answer

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.