I am working on 'how to access elements randomly from an array in javascript'. I found many links regarding this. Like:
Get random item from JavaScript array
var item = items[Math.floor(Math.random()*items.length)];
But in this, we can choose only one item from the array. If we want more than one elements then how can we achieve this? How can we get more than one element from an array?
Just two lines :
// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());
// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);
DEMO:
Try this non-destructive (and fast) function:
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
There is a one-liner unique solution here
array.sort(() => Math.random() - Math.random()).slice(0, n)
lodash _.sample and _.sampleSize.
Gets one or n random elements at unique keys from collection up to the size of collection.
_.sample([1, 2, 3, 4]);
// => 2
_.sampleSize([1, 2, 3], 2);
// => [3, 1]
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]
Getting 5 random items without changing the original array:
const n = 5;
const sample = items
.map(x => ({ x, r: Math.random() }))
.sort((a, b) => a.r - b.r)
.map(a => a.x)
.slice(0, n);
(Don't use this for big lists)
create a funcion which does that:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]);
}
return result;
}
you should also check if the sourceArray has enough elements to be returned. and if you want unique elements returned, you should remove selected element from the sourceArray.
Porting .sample from the Python standard library:
function sample(population, k){
/*
Chooses k unique random elements from a population sequence or set.
Returns a new list containing elements from the population while
leaving the original population unchanged. The resulting list is
in selection order so that all sub-slices will also be valid random
samples. This allows raffle winners (the sample) to be partitioned
into grand prize and second place winners (the subslices).
Members of the population need not be hashable or unique. If the
population contains repeats, then each occurrence is a possible
selection in the sample.
To choose a sample in a range of integers, use range as an argument.
This is especially fast and space efficient for sampling from a
large population: sample(range(10000000), 60)
Sampling without replacement entails tracking either potential
selections (the pool) in a list or previous selections in a set.
When the number of selections is small compared to the
population, then tracking selections is efficient, requiring
only a small set and an occasional reselection. For
a larger number of selections, the pool tracking method is
preferred since the list takes less space than the
set and it doesn't suffer from frequent reselections.
*/
if(!Array.isArray(population))
throw new TypeError("Population must be an array.");
var n = population.length;
if(k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
var result = new Array(k);
var setsize = 21; // size of a small set minus size of an empty list
if(k > 5)
setsize += Math.pow(4, Math.ceil(Math.log(k * 3) / Math.log(4)))
if(n <= setsize){
// An n-length list is smaller than a k-length set
var pool = population.slice();
for(var i = 0; i < k; i++){ // invariant: non-selected at [0,n-i)
var j = Math.random() * (n - i) | 0;
result[i] = pool[j];
pool[j] = pool[n - i - 1]; // move non-selected item into vacancy
}
}else{
var selected = new Set();
for(var i = 0; i < k; i++){
var j = Math.random() * n | 0;
while(selected.has(j)){
j = Math.random() * n | 0;
}
selected.add(j);
result[i] = population[j];
}
}
return result;
}
Implementation ported from Lib/random.py.
Notes:
setsize is set based on characteristics in Python for efficiency. Although it has not been adjusted for JavaScript, the algorithm will still function as expected.
Some other answers described in this page are not safe according to the ECMAScript specification due to the misuse of Array.prototype.sort. This algorithm however is guaranteed to terminate in finite time.
For older browsers that do not have Set implemented, the set can be replaced with an Array and .has(j) replaced with .indexOf(j) > -1.
Performance against the accepted answer:
https://jsperf.com/pick-random-elements-from-an-array
The performance difference is the greatest on Safari.
If you want to randomly get items from the array in a loop without repetitions you can remove the selected item from the array with splice:
var items = [1, 2, 3, 4, 5];
var newItems = [];
for (var i = 0; i < 3; i++) {
var idx = Math.floor(Math.random() * items.length);
newItems.push(items[idx]);
items.splice(idx, 1);
}
console.log(newItems);
ES6 syntax
const pickRandom = (arr,count) => {
let _arr = [...arr];
return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] );
}
I can't believe that no one didn't mention this method, pretty clean and straightforward.
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
Array.prototype.getnkill = function() {
var a = Math.floor(Math.random()*this.length);
var dead = this[a];
this.splice(a,1);
return dead;
}
//.getnkill() removes element in the array
//so if you like you can keep a copy of the array first:
//var original= items.slice(0);
var item = items.getnkill();
var anotheritem = items.getnkill();
Here's a nicely typed version. It doesn't fail. Returns a shuffled array if sample size is larger than original array's length.
function sampleArr<T>(arr: T[], size: number): T[] {
const setOfIndexes = new Set<number>();
while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
}
return Array.from(setOfIndexes.values()).map(i => arr[i]);
}
const randomIntFromInterval = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min + 1) + min);
In this answer, I want to share with you the test that I have to know the best method that gives equal chances for all elements to have random subarray.
Method 01
array.sort(() => Math.random() - Math.random()).slice(0, n)
using this method, some elements have higher chances comparing with others.
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Wrong Method */
const arr = myArray.sort(function() {
return val= .5 - Math.random();
});
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
Method 2
Using this method, the elements have the same probability:
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Correct Method */
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
The correct answer is posted in in the following link: https://stackoverflow.com/a/46545530/3811640
2020
non destructive functional programing style, working in a immutable context.
const _randomslice = (ar, size) => {
let new_ar = [...ar];
new_ar.splice(Math.floor(Math.random()*ar.length),1);
return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size);
}
console.log(_randomslice([1,2,3,4,5],2));
EDIT: This solution is slower than others presented here (which splice the source array) if you want to get only a few elements. The speed of this solution depends only on the number of elements in the original array, while the speed of the splicing solution depends on the number of elements required in the output array.
If you want non-repeating random elements, you can shuffle your array then get only as many as you want:
function shuffle(array) {
var counter = array.length, temp, index;
// While there are elements in the array
while (counter--) {
// Pick a random index
index = (Math.random() * counter) | 0;
// And swap the last element with it
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
var arr = [0,1,2,3,4,5,7,8,9];
var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements
DEMO: http://jsbin.com/UHUHuqi/1/edit
Shuffle function taken from here: https://stackoverflow.com/a/6274398/1669279
I needed a function to solve this kind of issue so I'm sharing it here.
const getRandomItem = function(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// original array
let arr = [4, 3, 1, 6, 9, 8, 5];
// number of random elements to get from arr
let n = 4;
let count = 0;
// new array to push random item in
let randomItems = []
do {
let item = getRandomItem(arr);
randomItems.push(item);
// update the original array and remove the recently pushed item
arr.splice(arr.indexOf(item), 1);
count++;
} while(count < n);
console.log(randomItems);
console.log(arr);
Note: if n = arr.length then basically you're shuffling the array arr and randomItems returns that shuffled array.
Demo
Here's an optimized version of the code ported from Python by #Derek, with the added destructive (in-place) option that makes it the fastest algorithm possible if you can go with it. Otherwise it either makes a full copy or, for a small number of items requested from a large array, switches to a selection-based algorithm.
// Chooses k unique random elements from pool.
function sample(pool, k, destructive) {
var n = pool.length;
if (k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3) / Math.log(4))))) {
if (!destructive)
pool = Array.prototype.slice.call(pool);
for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n)
var j = i + Math.random() * (n - i) | 0;
var x = pool[i];
pool[i] = pool[j];
pool[j] = x;
}
pool.length = k; // truncate
return pool;
} else {
var selected = new Set();
while (selected.add(Math.random() * n | 0).size < k) {}
return Array.prototype.map.call(selected, i => pool[i]);
}
}
In comparison to Derek's implementation, the first algorithm is much faster in Firefox while being a bit slower in Chrome, although now it has the destructive option - the most performant one. The second algorithm is simply 5-15% faster. I try not to give any concrete numbers since they vary depending on k and n and probably won't mean anything in the future with the new browser versions.
The heuristic that makes the choice between algorithms originates from Python code. I've left it as is, although it sometimes selects the slower one. It should be optimized for JS, but it's a complex task since the performance of corner cases is browser- and their version-dependent. For example, when you try to select 20 out of 1000 or 1050, it will switch to the first or the second algorithm accordingly. In this case the first one runs 2x faster than the second one in Chrome 80 but 3x slower in Firefox 74.
Sampling with possible duplicates:
const sample_with_duplicates = Array(sample_size).fill().map(() => items[~~(Math.random() * items.length)])
Sampling without duplicates:
const sample_without_duplicates = [...Array(items.length).keys()].sort(() => 0.5 - Math.random()).slice(0, sample_size).map(index => items[index]);
Since without duplicates requires sorting the whole index array first, it is considerably slow than with possible duplicates for big items input arrays.
Obviously, the max size of without duplicates is <= items.length
Check this fiddle: https://jsfiddle.net/doleron/5zw2vequ/30/
It extracts random elements from srcArray one by one while it get's enough or there is no more elements in srcArray left for extracting.
Fast and reliable.
function getNRandomValuesFromArray(srcArr, n) {
// making copy to do not affect original srcArray
srcArr = srcArr.slice();
resultArr = [];
// while srcArray isn't empty AND we didn't enough random elements
while (srcArr.length && resultArr.length < n) {
// remove one element from random position and add this element to the result array
resultArr = resultArr.concat( // merge arrays
srcArr.splice( // extract one random element
Math.floor(Math.random() * srcArr.length),
1
)
);
}
return resultArr;
}
Here's a function I use that allows you to easily sample an array with or without replacement:
// Returns a random sample (either with or without replacement) from an array
const randomSample = (arr, k, withReplacement = false) => {
let sample;
if (withReplacement === true) { // sample with replacement
sample = Array.from({length: k}, () => arr[Math.floor(Math.random() * arr.length)]);
} else { // sample without replacement
if (k > arr.length) {
throw new RangeError('Sample size must be less than or equal to array length when sampling without replacement.')
}
sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]);
};
return sample;
};
Using it is simple:
Without Replacement (default behavior)
randomSample([1, 2, 3], 2) may return [2, 1]
With Replacement
randomSample([1, 2, 3, 4, 5, 6], 4) may return [2, 3, 3, 2]
var getRandomElements = function(sourceArray, requiredLength) {
var result = [];
while(result.length<requiredLength){
random = Math.floor(Math.random()*sourceArray.length);
if(result.indexOf(sourceArray[random])==-1){
result.push(sourceArray[random]);
}
}
return result;
}
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1, 2, 3, 4, 5];
const fetchRandomArray = ({pool=[], limit=1})=>{
let query = []
let selectedIndices = {}
while(query.length < limit){
const index = Math.floor(Math.random()*pool.length)
if(typeof(selectedIndices[index])==='undefined'){
query.push(items[index])
selectedIndices[index] = index
}
}
console.log(fetchRandomArray({pool:items, limit:10})
2019
This is same as Laurynas Mališauskas answer, just that the elements are unique (no duplicates).
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
Now to answer original question "How to get multiple random elements by jQuery", here you go:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
var $set = $('.someClass');// <<<<< change this please
var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
allIndexes.push(i);
}
var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);
var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
var randomIndex = randomIndexes[i];
if($randomElements === null) {
$randomElements = $set.eq(randomIndex);
} else {
$randomElements.add($set.eq(randomIndex));
}
}
// $randomElements is ready
$randomElements.css('backgroundColor', 'red');
Here is the most correct answer and it will give you Random + Unique elements.
function randomize(array, n)
{
var final = [];
array = array.filter(function(elem, index, self) {
return index == self.indexOf(elem);
}).sort(function() { return 0.5 - Math.random() });
var len = array.length,
n = n > len ? len : n;
for(var i = 0; i < n; i ++)
{
final[i] = array[i];
}
return final;
}
// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this
items.sort(() => (Math.random() > 0.5 ? 1 : -1)).slice(0, count);
So I need to get the highest 3 values from an array, though I don't know how many values I need to check since that number will change periodically, I tried running this loop but it doesn't work.
numbers = new array();
numbers = (6,34,6623,22,754,2677,12)
var num1 = 0
var num2 = 0
var num3 = 0
for (var i=0; i<numbers.length;i++) {
if num1<numbers[i] {
num1 = numbers[i]
} else if num2<numbers[i] {
num2 = numbers[i]
} else if num3<numbers[i] {
num3 = numbers[i]
}
}
All of the other answers propose the correct and fast way to do this, i.e. to sort the array in descending order and take the first three elements.
If you're interested why your code did not work: The problem is that you need to keep track of each of the last found max values if a new one is found. You need to change your code to something like this:
const numbers = [6, 34, 6623, 22, 754, 2677, 12]
let max = 0;
let oneLowerToMax = 0;
let twoLowerToMax = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > max) {
twoLowerToMax = oneLowerToMax;
oneLowerToMax = max;
max = numbers[i];
}else if(numbers[i] > oneLowerToMax) {
twoLowerToMax = oneLowerToMax;
oneLowerToMax = numbers[i];
}else if(numbers[i] > twoLowerToMax) {
twoLowerToMax = numbers[i];
}
}
console.log(max, oneLowerToMax, twoLowerToMax) // prints 6623, 2677, 754
Sort the array in descending order and then get all 3 element by its index:
let numbers = new Array(6,34,6623,22,754,2677,12);
numbers.sort(function(a, b){return b-a});
console.log(numbers[0], numbers[1], numbers[2]);
you can sort array
var numbers = [1,65,8,98,689,12,33,2,3,789];
var topValues = [... numbers].sort((a,b) => b-a).slice(0,3);
there is a simpler way you can achieve this.
first, run a sort() on your array, then pick the highest ones.
const myArray = [7,4,99,10,55,1];
console.log(myArray.sort((a,b)=>b-a).slice(0, 3)) // [99, 55, 10]
Simply sort your array in descending order, then slice the first three values.
Note: .sort().reverse() is the speediest approach for sorting in descending order. See https://stackoverflow.com/a/52030227/129086
function getTopThree(arr) {
// spread avoids modifying the original array
return [...arr].sort().reverse().slice(0, 3);
}
getTopThree(numbers);
Whenever your numbers array is updated or changed, call getTopThree(numbers) again.
I've got a assignment to make function that gives you a sorted array with 6 random numbers from 1 to 45. None of the array values should equal to one another.
I thought about a solution that would work in Java but the JavaScript console logs I get are pretty confusing. Could anyone help me out?
"use strict";
var numbers = [];
for(var x = 1; x <46;x++){
numbers.push(x);
}
function LottoTipp(){
var result = [];
for(var i = 0; i <6; i++){
var randomNum = Math.round(Math.random()* 45);
var pushed = numbers[randomNum];
result.push(pushed);
numbers.splice(randomNum)
}
return console.log(result) + console.log(numbers);
}
LottoTipp();
the console logs
[ 34, 7, undefined, undefined, undefined, undefined ]
[ 1, 2, 3, 4, 5, 6 ]
There were three problems:
If you want to remove one item of an array you have to splice it by the items index and give a deletecount.
In your case: numbers.splice(randomNum, 1);
You have to use Math.floor instead of Math.round, because Math.floor always down to the nearest integerer, while Math.round searches for the nearest integer wich could be higher than numbers.length.
After removing one item the length of the array has been changed. So you have to multiply by numbers.lenght instead of 45.
In your case: var randomNum = Math.floor(Math.random()* numbers.length);
"use strict";
var numbers = [];
for(var x = 1; x < 46; x++){
numbers.push(x);
}
function LottoTipp(){
var result = [];
for(var i = 0; i < 6; i++){
var randomNum = Math.floor(Math.random()* numbers.length);
var pushed = numbers[randomNum];
result.push(pushed);
numbers.splice(randomNum, 1);
}
return console.log(result) + console.log(numbers);
}
LottoTipp();
If you only want an array with random unique numbers I would suggest doing it like this:
<script>
var array = [];
for(i = 0; i < 6; i++) {
var number = Math.round(Math.random() *45);
if(array.indexOf(number) == -1) { //if number is not already inside the array
array.push(number);
} else { //if number is inside the array, put back the counter by one
i--;
}
}
console.log(array);
</script>
There is no issue with the console statement the issue is that you are modifying the numbers array in your for loop. As you are picking the random number between 1-45 in this statement:-
var randomNum = Math.round(Math.random()* 45);
and you expect that value would be present in the numbers array at that random index. However you are using array.splice() and providing only first parameter to the function. The first parameter is the start index from which you want to start deleting elements, find syntax here. This results in deleting all the next values in the array.Therefore if you pick a random number number say.. 40, value at numbers[40] is undefined as you have deleted contents of the array.
if you want to generate unique set of numbers follow this post.
hope it helps!
Just add the number in the result if it is unique otherwise take out a new number and then sort it. Here is an implementation:
let result = []
while(result.length < 6) {
let num = Math.round(Math.random() * 45);
if(!result.includes(num)) {
result.push(num);
}
}
result.sort((a,b) => {
return parseInt(a) - parseInt(b);
});
console.log(result);
I need to iterate from 0 to 30, but I want to do this with help of forEach:
new Array(30).forEach(console.log.bind(console);
Of course this does not work, therefor I do:
new Array(30).join(',').split(',').forEach(console.log.bind(console));
Is there other ways to fill empty arrays?
Actually, there's a simple way to create a [0..N) (i.e., not including N) range:
var range0toN = Object.keys(Array.apply(0,Array(N)));
Apparently Object.keys part can be dropped if you only want to get a proper array of N elements.
Still, like others said, in this particular case it's probably better to use for loop instead.
if you want all of item have same value, do this
var arrLength = 4
var arrVal = 0
var newArr = [...new Array(arrLength)].map(x => arrVal);
// result will be [0, 0, 0, 0]
You could try using a for loop. new Array is not a best practise
var index, // we use that in the for loop
counter, // number of elements in array
myArray; // the array you want to fill
counter = 30;
myArray = [];
for (index = 0; index < counter; index += 1) {
myArray[index] = [];
/*
// alternative:
myArray.push([]);
// one-liner
for (index = 0; index < counter; index += 1) myArray.push([]);
*/
}
If you simply want to iterate, then use for loop like this
for (var i = 0; i < 30; i += 1) {
...
...
}
Actually, if you are looking for a way to create a range of numbers, then you can do
console.log(Array.apply(null, {length: 30}).map(Number.call, Number));
It will create numbers from 0 to 29. Source : Creating range in JavaScript - strange syntax
If you insist foreach
var data = [1, 2, 3];
data.forEach(function(x) {
console.log(x);
});
I have an array like this
[0,2,3]
The possible shuffling of this array are
[0,2,3], [2,3,0], [3,0,2], [3,2,0], [0,3,2], [2,0,3]
How can I get these combinations? The only idea I have in mind currently is
n = maximum num of possible combinations, coms = []
while( coms.length <= n )
temp = shuffle( original the array );
if temp is there in coms
return
else
coms.push(temp);
But I do not think this is efficient, as here we are blindly depending on uniform distribution of shuffle method.
Is there alternative findings for this problem?
The first thing to note is that the number of permutations increases very fast with regard to the number of elements (13 elements = 6 bilion permutations), so any kind of algorithm that generates them will deteriorate in performance for a large enough input array.
The second thing to note is that since the number of permutations is very large, storing them in memory is expensive, so you're way better off using a generator for your permutations and doing stuff with them as they are generated.
The third thing to note is that recursive algorithms bring a large overhead, so even if you find a recursive solution, you should strive to get a non-recursive one. Obtaining a non-recursive solution if a recursive one exists is always possible, but it may increase the complexity of the code.
I have written a non recursive implementation for you, based on the Steinhaus–Johnson–Trotter algorithm (http://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm)
function swap(arr, a,b){
var temp = arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
function factorial(n) {
var val = 1;
for (var i=1; i<n; i++) {
val *= i;
}
return val;
}
function permute(perm, func){
var total = factorial(perm.length);
for (var j=0, i=0, inc=1; j<total; j++, inc*=-1, i+=inc) {
for (; i<perm.length-1 && i>=0; i+=inc) {
func.call(perm);
swap (perm, i, i+1);
}
func.call(perm);
if (inc === 1) {
swap(perm, 0,1);
} else {
swap(perm, perm.length-1, perm.length-2);
}
}
}
console.clear();
count = 0;
permute([1,2,3,4,5,6], function(){console.log(this); count++;});
console.log('There have been ' + count + ' permutations');
http://jsbin.com/eXefawe/2/edit
Try a recursive approach. Here's a hint: every permutation of [0,2,3] is either
[0] plus a permutation of [2,3] or
[2] plus a permutation of [0,3] or
[3] plus a permutation of [0,2]
As zodiac mentioned the best solution to this problem is a recursive one:
var permute = (function () {
return permute;
function permute(list) {
return list.length ?
list.reduce(permutate, []) :
[[]];
}
function permutate(permutations, item, index, list) {
return permutations.concat(permute(
list.slice(0, index).concat(
list.slice(index + 1)))
.map(concat, [item]));
}
function concat(list) {
return this.concat(list);
}
}());
alert(JSON.stringify(permute([1,2,3])));
Hope that helps.
All permutations of a set can be found by selecting an element in the set and recursively permuting (rearranging) the remaining elements. Backtracking approach can be used for finding the solution.
Algorithm steps (source):
Pseudocode (source):
permute(i)
if i == N output A[N]
else
for j = i to N do
swap(A[i], A[j])
permute(i+1)
swap(A[i], A[j])
Javascript implementation (jsFiddle):
Array.prototype.clone = function () {
return this.slice(0);
};
var input = [1, 2, 3, 4];
var output = [];
function permute(i) {
if (i == input.length)
output.push(input.clone());
else {
for (var j = i; j < input.length; j++) {
swap(i, j);
permute(i + 1);
swap(i, j); // backtrack
}
}
};
function swap(i, j) {
var temp = input[i];
input[i] = input[j];
input[j] = temp;
}
permute(0);
console.log(output);
For an array of length n, we can precompute the number of possible permutations. It's n! (n factorial)
function factorial(n){ return n<=0?1:n*factorial(n-1);}
//There are better ways, but just for illustration's sake
And, we can create a function which maps an integer p between 0...n!-1 to a distinct permutation.
function map(p,orgArr){
var tempArr=orgArr.slice(); //Create a copy
var l=orgArr.length;
var permArr=[];
var pick;
do{
pick=p%l; //mod operator
permArr.push(tempArr.splice(pick,1)[0]); //Remove item number pick from the old array and onto the new
p=(p-pick)/l;
l--;
}while(l>=1)
return permArr;
}
At this point, all you need to do is create an array ordering=[0,1,2,3,...,factorial(n)-1] and shuffle that. Then, you can loop for(var i=0;i<=ordering.length;i++) doSomething(map(ordering[i],YourArray));
That just leaves the question of how to shuffle the ordering array. I believe that's well documented and outside the scope of your question since the answer depends on your application (i.e. is pseudo random good enough, or do you need some cryptographic strength, speed desired, etc...). See How to randomize (shuffle) a JavaScript array? and many others.
Or, if the number of permutations is so large that you don't want to create this huge ordering array, you just have to distinctly pick values of i for the above loop between 0-n!-1. If just uniformity is needed, rather than randomness, one easy way would be to use primitive roots: http://en.wikipedia.org/wiki/Primitive_root_modulo_n
you don't need to recurse. The demo should make the pattern pretty clear: http://jsfiddle.net/BGYk4/
function shuffle(arr) {
var output = [];
var n = arr.length;
var ways = [];
for(var i = 0, j = 1; i < n; ways.push(j *= ++i));
var totalWays = ways.pop();
for(var i = 0; i < totalWays; i++) {
var copy = arr.slice();
output[i] = [];
for(var k = ways.length - 1; k >= 0; k--) {
var c = Math.floor(i/ways[k]) % (k + 2);
output[i].push(copy.splice(c,1)[0]);
}
output[i].push(copy[0]);
}
return output;
}
this should output all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
this one's cuter (but the output from the other example is much clearer): http://jsfiddle.net/wCnLf/
function shuffle(list) {
var shufflings = [];
while(true) {
var clone = list.slice();
var shuffling = [];
var period = 1;
while(clone.length) {
var index = Math.floor(shufflings.length / period) % clone.length;
period *= clone.length;
shuffling.push(clone.splice(index,1)[0]);
}
shufflings.push(shuffling);
if(shufflings.length == period) return shufflings;
}
}
and of course it still outputs all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
tibos example above was exactly what I was looking for but I had some trouble running it, I made another solution as an npm module:
var generator = new Permutation([1, 2, 3]);
while (generator.hasNext()) {
snippet.log(generator.next());
}
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="http://rawgit.com/bcard/iterative-permutation/master/iterative-permutation.js"></script>
https://www.npmjs.com/package/iterative-permutation
https://github.com/bcard/iterative-permutation