Generate an array of unique, non-repeating elements in Javascript
Link to original article on Medium here
In a recent project, one of my tasks was to display images from an API of product data. The goal was to choose the products at random, without showing duplicates. I started by using Math.random() on the original API array, but that allowed for dupes, so some of the images were repeating themselves on the page.
Say we have an array of API data with five array elements:
[‘lipstick’, ‘lipgloss’, ‘eyeliner’, ‘blush’, ‘moisturizer’]
and we want to generate a new array with three randomly selected, non-repeating array elements.
For example, we’d want this:
[‘blush’, ‘moisturizer’, ‘lipstick’] because the elements aren’t repeating themselves.
… but we would NOT want this:
[‘lipgloss’, ‘blush’, ‘lipgloss’] because an element (lipgloss) is repeating itself.
The key is to ensure that each time a random element is selected, it is removed from the pool of selectable data.
Here’s how I solved for that:
const apiArray = ['lipstick', 'lipgloss', 'eyeliner', 'blush', 'moisturizer']
function genRandomElements(list) {
? let arrayCopy = [...list];
? let newArray = [];
? for(let i = 0; i < 3; i++) {
? ? let randNum = Math.floor(Math.random()*arrayCopy.length);
? ? let splicedItem = arrayCopy.splice(randNum, 1)[0]
? ? newArray.push(splicedItem);
? }
? return newArray;
}
genRandomElements(apiArray);
The API array contains the data from which we’d like to generate a new array of 3 random, non-repeating elements. We declare it with variable apiArray.
Then we declare the function genRandomElements with?list?as the parameter. In our case, we will be passing apiArray as the argument, so that when we call the function on our apiArray, it will look like this:
genRandomElements(apiArray);
We start this function by creating two new arrays. The first is a copy of the API array, called arrayCopy, which is created by using the spread operator on the passed-in parameter, apiArray (see […list]). The reason we’re making a copy is because we’ll be using splice(), which is a destructive method, to pick random elements from the arrayCopy and we don’t want to make changes to the actual API array.
The second newly created array is an empty array and is assigned the variable newArray. This is the array that we will be inserting the randomly selected, non-repeating elements from arrayCopy into.
We use a for loop to tell the Javascript engine to loop through arrayCopy three times. Each time it goes through the loop, it is to remove a randomly-selected array element from arrayCopy, and ‘push’ that array element into the newArray. Following the first loop-through, in each subsequent loop-through, the item(s) that was (were) selected in the previous loop-through(s) is (are) no longer inside of arrayCopy, so they cannot be selected again. After looping through three times, our newArray array will be populated with 3 randomly selected elements from the arrayCopy.
? for(let i = 0; i < 3; i++) {
? ? let randNum = Math.floor(Math.random()*arrayCopy.length);
? ? let splicedItem = arrayCopy.splice(randNum, 1)[0]
? ? newArray.push(splicedItem);
? }
The for loop will start at i=0 and will increment until i is no longer less than 3. In other words, it will run the code in the curly brackets {} three times (count i = 0, i = 1, and i = 2).
Inside the curly brackets, first we assign Math.floor(Math.random()*arrayCopy.length to the variable randNum.
In our example, randNum will be a random integer between 0 and 4, inclusive, because:
Math.random() generates a random number between 0 and 1, inclusive of 0, but not inclusive of 1. Ie 0 ≤ Math.random() < 1. It could be 0.8988672771925821, for example.
arrayCopy.length is the length of the arrayCopy (which is a copy of the API array). In our example, it has a length of 5 (there are five elements in it).
When we multiply Math.random() by arrayCopy.length, it results in a number between 0 and the length of the array. So continuing our example, it could be: 0.8988672771925821*5 = 4.49433638596291.
Since our purpose is to select a random element from arrayCopy, and we know that array elements are indexed as integers starting from zero (index 0 is the first element, index 1 is the second element, index n is the (n+1)th element, etc…), we need to make sure that the random number we generate is an integer.
By applying Math.floor(), we are forcing the random number to be an integer between 0 and 4 inclusive. Note that our array has 5 elements, indexed from 0 to 4. Continuing with the example above, Math.floor(4.49433638596291) would return 4.
So, randNum is a randomly generated integer within the range of the index numbers of the array from which we’d like to take elements, which is just what we want.
Splice is a destructive method that changes the contents of an array by removing existing elements, returning a new array which contains the elements that were removed from the array that splice() was applied to.
领英推荐
We use randNum as the starting parameter for splice on the arrayCopy. Then we use 1 as the deleteCount parameter.
arrayCopy.splice(randNum, 1)
Before the splice, arrayCopy looks like this:
[‘lipstick’, ‘lipgloss’, ‘eyeliner’, ‘blush’, ‘moisturizer’]
If we plug in our example, where randNum is 4, we have arrayCopy.splice(4, 1), which removes the element at index 4 from arrayCopy, and creates a separate array with the removed element.
In arrayCopy, index 4 is ‘moisturizer’
Therefore,
arrayCopy after the splice now looks like this:
[‘lipstick’, ‘lipgloss’, ‘eyeliner’, ‘blush’]
and
arrayCopy.splice(4, 1) = [‘moisturizer’]
arrayCopy.splice(randNum, 1) is itself an array containing the element that, before the splice, was indexed at randNum (4 in the above example) in arrayCopy.
That’s the element that we want placed into newArray.
To access the element, and not the whole array, we place the square brackets with zero inside ([0]) to the end of the new array that was created as a result of splicing arrayCopy: arrayCopy.splice(randNum, 1)[0]
Again with the running example, see that
arrayCopy.splice(4, 1) = [‘moisturizer’]
Therefore,
arrayCopy.splice(4, 1)[0] = ‘moisturizer’
By adding [0], we are specifying that we want the element at index 0.
arrayCopy.splice(randNum, 1)[0] is exactly what we want placed into newArray. We assign it to the variable splicedItem.
We then use the push() method to insert the splicedItem into the newArray.
Amazing. newArray now contains a random element from arrayCopy.
Then we close our loop. That marks one loop through. The for loop will then increment by 1, and go through again.
The next time it runs through, it will not be able to select the previously selected element. That’s because by splicing, we removed the element from arrayCopy.
In our case, since we set the loop to end at i < 3, it will loop through two more times.
After the final loop, it leaves the code block and finishes the function by returning the newArray, which now contains 3 randomly selected elements from the arrayCopy array.
I hope this was helpful.
Cheers,
Will
REGAIN Your "Lost 6-9 Figures" with Our P.G.F. Campaign + All-in-One-Stop Tech + 24/7/365 AI ?? 2X Your ARR across Multiple Locations Annually without 10+ Years of Trial-and-Error and 0 ROI Staff ??
2 年What a great post Will
Advocate for the expansion of female, black, and Latino software engineers as our profession needs these minds.
2 年Nice job on this Will!
Matchmaker, Recruiter, Friend... I am dedicated to finding the most qualified and suitable candidates for your organization.
2 年Really thank you for writing about it, it’s awesome!
Lead Data Architect at Reactionpower Inc.
2 年Well done, Will!
Jeweller ??-Designer and Silversmith ? Also teach Spanish, ole! ??
2 年Thank you for sharing.