Estoy trabajando en 'cómo acceder a elementos aleatoriamente desde una matriz en javascript'. Encontré muchos enlaces al respecto. Me gusta: obtener un elemento aleatorio de la matriz de JavaScript
var item = items[Math.floor(Math.random()*items.length)];
Pero en esto, podemos elegir solo un elemento de la matriz. Si queremos más de un elemento, ¿cómo podemos lograrlo? ¿Cómo podemos obtener más de un elemento de una matriz?
javascript
jquery
html
arrays
Shyam Dixit
fuente
fuente
Respuestas:
Pruebe esta función no destructiva (y rápida ):
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; }
fuente
Set
(que no estaba disponible en '13: - /)Solo dos líneas:
// 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 :
fuente
let random = array.sort(() => .5 - Math.random()).slice(0,n)
Aquí hay una solución única de una sola línea
array.sort(() => Math.random() - Math.random()).slice(0, n)
fuente
Portando
.sample
desde la biblioteca estándar de Python: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, 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; }
Implementación portada de Lib / random.py .
Notas:
setsize
se establece en función de las características de Python para la eficiencia. Aunque no se ha ajustado para JavaScript, el algoritmo seguirá funcionando como se esperaba.Array.prototype.sort
. Sin embargo, se garantiza que este algoritmo terminará en un tiempo finito.Set
implementado, el conjunto se puede reemplazar conArray
y.has(j)
reemplazar con.indexOf(j) > -1
.Desempeño contra la respuesta aceptada:
fuente
Obteniendo 5 elementos aleatorios sin cambiar la matriz original:
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);
(No use esto para listas grandes)
fuente
crea una función que hace eso:
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; }
También debe verificar si sourceArray tiene suficientes elementos para ser devueltos. y si desea que se devuelvan elementos únicos, debe eliminar el elemento seleccionado de sourceArray.
fuente
sourceArray
varias veces.Sintaxis de ES6
const pickRandom = (arr,count) => { let _arr = [...arr]; return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] ); }
fuente
Si desea obtener elementos de la matriz al azar en un bucle sin repeticiones, puede eliminar el elemento seleccionado de la matriz con
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);
fuente
1
es eldeleteCount
que indica el número de elementos de matriz viejos eliminar. (Por cierto, reduje las dos últimas líneas anewItems.push(items.splice(idx, 1)[0])
).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();
fuente
Aquí hay una versión muy bien escrita. No falla. Devuelve una matriz mezclada si el tamaño de la muestra es mayor que la longitud de la matriz original.
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);
fuente
lodash ( https://lodash.com/ )
_.sample
y_.sampleSize
.Obtiene uno o n elementos aleatorios en claves únicas desde la colección hasta el tamaño de la colección.
_.sample([1, 2, 3, 4]); // => 2 _.sampleSize([1, 2, 3], 2); // => [3, 1] _.sampleSize([1, 2, 3], 4); // => [2, 3, 1]
fuente
_
? No es un objeto estándar de JavaScript.EDITAR : Esta solución es más lenta que otras presentadas aquí (que empalman la matriz de origen) si desea obtener solo unos pocos elementos. La velocidad de esta solución depende solo del número de elementos de la matriz original, mientras que la velocidad de la solución de empalme depende de la cantidad de elementos necesarios en la matriz de salida.
Si desea elementos aleatorios que no se repitan, puede mezclar su matriz y luego obtener solo tantos como desee:
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
Función aleatoria tomada de aquí: https://stackoverflow.com/a/6274398/1669279
fuente
O(n+k)
(n elementos en la matriz, quiere k de ellos) mientrasO(k)
que sería posible (y óptimo).O(2n)
que podría reducirseO(n+k)
si cambiara el bucle aywhile (counter-- > len-k)
eliminara los últimos elementos (en lugar de los primeros)k
. De hechosplice(i, 1)
no lo tieneO(1)
, peroO(k)
aún es posible una solución (vea mi respuesta). Sin embargo, la complejidad del espacio se mantieneO(n+k)
desafortunadamente, pero podríaO(2k)
depender de la implementación del arreglo disperso.Necesitaba una función para resolver este tipo de problema, así que la comparto aquí.
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);
Nota: si
n = arr.length
entonces básicamente estás barajando la matrizarr
yrandomItems
devuelve esa matriz barajada.Manifestación
fuente
En esta respuesta, quiero compartir con ustedes la prueba de que tengo que conocer el mejor método que brinda las mismas posibilidades de que todos los elementos tengan un subarreglo aleatorio.
Método 01
array.sort(() => Math.random() - Math.random()).slice(0, n)
Al utilizar este método, algunos elementos tienen mayores posibilidades en comparación con otros.
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)
Método 2
Usando este método, los elementos tienen la misma probabilidad:
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)
La respuesta correcta se publica en el siguiente enlace: https://stackoverflow.com/a/46545530/3811640
fuente
Aquí hay una versión optimizada del código portado desde Python por @Derek, con la opción destructiva agregada (en el lugar) que lo convierte en el algoritmo más rápido posible si puede seguirlo. De lo contrario, realiza una copia completa o, para una pequeña cantidad de elementos solicitados de una matriz grande, cambia a un algoritmo basado en selección.
// 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, 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 => population[i]); } }
En comparación con la implementación de Derek, el primer algoritmo es mucho más rápido en Firefox y un poco más lento en Chrome, aunque ahora tiene la opción destructiva, la de mayor rendimiento. El segundo algoritmo es simplemente un 5-15% más rápido. Intento no dar números concretos, ya que varían según kyn y probablemente no signifiquen nada en el futuro con las nuevas versiones del navegador.
La heurística que hace la elección entre algoritmos se origina en el código Python. Lo dejé como está, aunque a veces selecciona el más lento. Debería estar optimizado para JS, pero es una tarea compleja ya que el rendimiento de los casos de esquina depende del navegador y de su versión. Por ejemplo, cuando intente seleccionar 20 de 1000 o 1050, cambiará al primer o segundo algoritmo en consecuencia. En este caso, el primero se ejecuta 2 veces más rápido que el segundo en Chrome 80, pero 3 veces más lento en Firefox 74.
fuente
log(k*3, 4)
ya que JS no tiene elbase
argumento. En caso de serlog(k*3)/log(4)
pool
comoresult
. Como lo truncapool
, ya no se puede usar como fuente para muestrear y la próxima vez que lo usesample
tendrá que volver a crear apool
partir de alguna fuente. La implementación de Derek solo baraja el grupo, por lo que se puede reutilizar perfectamente para muestrear sin volver a crear. Y creo que este es el caso de uso más frecuente.Estilo de programación funcional 2020 no destructivo, trabajando en un contexto inmutable.
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));
fuente
_shuffle
funcion?Extrae elementos aleatorios de srcArray uno por uno mientras obtiene suficiente o no quedan más elementos en srcArray para extraer. Rápido y confiable.
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; }
fuente
2019
Esto es lo mismo que la respuesta de Laurynas Mališauskas , solo que los elementos son únicos (sin duplicados).
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; }
Ahora, para responder a la pregunta original "Cómo obtener varios elementos aleatorios con jQuery", aquí tienes:
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');
fuente
Aquí hay una función que uso que le permite muestrear fácilmente una matriz con o sin reemplazo:
// 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; };
Usarlo es simple:
Sin reemplazo (comportamiento predeterminado)
randomSample([1, 2, 3], 2)
puede volver[2, 1]
Con reemplazo
randomSample([1, 2, 3, 4, 5, 6], 4)
puede volver[2, 3, 3, 2]
fuente
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; }
fuente
No puedo creer que nadie no haya mencionado este método, bastante limpio y sencillo.
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
fuente
Aquí está la respuesta más correcta y le dará elementos aleatorios + únicos.
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
fuente
items.sort (() => (Math.random ()> 0.5? 1: -1)). slice (0, count);
fuente