martes, 27 de agosto de 2019

JavaScript: generar números sin repetición

Queremos crear un generador de números de la lotería Primitiva. Se trata de generar seis números entre 1 y 49 de forma aleatoria, sin repetición.

Versión 1

El código está disponible en el siguiente enlace.

HTML

En un div vacío es donde volcaremos la combinación ganadora de la lotería.

 <!DOCTYPE html>  
 <html>  
  <head>  
   <meta charset="utf-8">  
   <meta name="viewport" content="width=device-width">  
   <title>Loteria Primitiva con JavaScript</title>  
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>  
   <script type="text/javascript" src="script.js"></script>  
   <link href="style.css" rel="stylesheet" type="text/css" />  
  </head>  
  <body>  
   <h1>Lotería primitiva</h1>  
   <h3>Seis números entre 1 y 49 sin repetición</h3>  
   <p class='big'>  
    Programación en JavaScript y jQuery<br>  
    Caso práctico  
   </p>  
   <button>Generar combinación</button>  
   <br>  
   <hr>  
   <br>  
   <div></div>  
  </body>  
 </html>  

CSS

Un poco de color y estilo con CSS. Lo más interesante es word-spacing: 20px; que nos permite espaciar las palabras de una frase. En este caso, no son palabras sino los números de la combinación ganadora que imprimimos en el div. Si esto no se pusiera todos los números se verían sin mucho espacio entre ellos.

 h1 {  
  color: green;  
  letter-spacing: 3px;  
 }  
 h3 {  
  color: purple;  
 }  
 p.big {  
  color: blue;  
  line-height: 1.8;  
 }  
 div {  
  color: red;  
  text-indent: 30px;  
  word-spacing: 20px;  
  text-shadow: 2px 1px pink;  
  font-size: 24px;  
  font-family: Arial, Helvetica, sans-serif;  
 }  

JavaScript + jQuery

Creamos la variable lista que es un array que contendrá los seis números aleatorios sin repetición. Inicializamos la variable con uno de los números aleatorios, ya que el primero de ellos estamos seguros de que no está repetido.

Entramos en un bucle while que se repetirá mientras la lista no llegue a seis elementos.

Creamos un número candidato que es otro número aleatorio entre 1 y 49. Ahora debemos averiguar si este candidato se encuentra ya entre los números que contiene el array hasta el momento. Para ello necesitaremos una variable booleana de control denominada norepe que inicializamos en true, ya que estamos presuponiendo que el candidato no está repetido.

Entramos en otro bucle while que se repetirá mientras norepe sea true, esto es, el bucle se repetirá mientras el candidato no supere el test que nos garantiza que es único.

El test que debe pasar cada candidato es un bucle forEach con una función anónima que contiene un condicional if donde evaluamos si el candidato es igual a cada uno de los elementos (item) que contiene hasta el momento la lista. En caso de encontrar alguna repetición la variable de control booleana norepe se convierte en false, lo que indica que se ha encontrado una repetición.

Finalizado el test nos preguntamos con un if si norepe es true, en caso afirmativo quiere decir que el candidato ha superado el test y que por lo tanto es único y será incorporado a la lista con push, que lo añade al final.

En caso de que el candidato no supere el test (en caso de que esté repetido) se genera un nuevo número aleatorio candidato y comienza el proceso, hasta que se consiga obtener uno que sea único y así podamos incorporarlo a la lista.

Finalmente se ordena la lista con sort para ser impresa en pantalla incorporándose al div. Con finalidad estética convertimos la lista en un string usando join(' ') con un separador de espacio. Si no pusiéramos el espacio se verían todos los números de la combinación ganadora juntos, apelotonados, sin separación, y no podría actuar el código CSS separándolos aún más.

 'use strict'  
 $(function(){  
  $("button").click(function(){  
   var lista=[Math.floor(Math.random()*(49-1))+1];  
   console.log("longitud:"+lista.length,"lista: ",lista);  
   while (lista.length<6){  
    var candidato=Math.floor(Math.random()*(49-1))+1;  
    console.log("candidato:",candidato);  
    var norepe=true;//inicialmente suponemos que no está repetido  
    while (norepe){  
     lista.forEach(function(item){  
      if(candidato==item){  
       norepe=false; //detectado nº repetido  
      };  
     });  
     if (norepe){  
      lista.push(candidato); //append al array  
      console.log("longitud:"+lista.length,"lista: ",lista);  
     };  
    };  
   };  
   lista.sort((a,b)=>a-b); //función de flecha para ordenar números bien  
   $('div').html(lista.join(' ')); //convertir array to string  
  });  
 });  

Cuando el candidato está repetido se busca otro.

Versión 1 bis

Vamos a organizar el código JS anterior incorporando una función denominada comprueba que se encargará de verificar que el número candidato no está ya en la lista, y en caso de que no se encuentre repetido lo incorporará al final del array.
 'use strict'  
 $(function(){  
  $("button").click(function(){  
   var lista=[Math.floor(Math.random()*(49-1))+1];  
   console.log("longitud:"+lista.length,"lista: ",lista);  
   while (lista.length<6){  
    var candidato=Math.floor(Math.random()*(49-1))+1;  
    console.log("candidato:",candidato);  
    //invocamos la función que comprueba  
    //si el candidato no está repetido lo añade a la lista  
    comprueba(candidato, lista);  
   };  
   lista.sort((a,b)=>a-b); //función de flecha para ordenar números bien  
   $('div').html(lista.join(' ')); //convertir array to string  
   function comprueba(candidato,lista){  
    let norepe=true;//inicialmente suponemos que no está repetido  
    while (norepe){  
     lista.forEach(function(item){  
      if(candidato==item){norepe=false};//detectado nº repetido  
     });  
     if (norepe){//si el candidato no está repetido  
      lista.push(candidato); //se añade a la lista  
      console.log("longitud:"+lista.length,"lista: ",lista);  
     };  
    };  
    return lista;  
   }  
  });  
 });  

Versión 2

El código está disponible en el siguiente enlace.

JavaScript + jQuery

Creamos la variable lista 49 que es un array que contiene los números correlativos 1 hasta 49.

var lista49=[1,2,3,4,5,6,7,8, ... ... ... , 49];

La forma de crear esta lista consiste en crear un array vacío de 49 posiciones y luego con una función de flecha asignar el valor correlativo de cada elemento al índice que ocupa k más 1. Hemos de sumar 1 ya que los arrays comienzan en cero y nosotros queremos que el primer valor sea 1.
La lista con los 49 números correlativos se desordena con sort y una función de flecha. El método sort permite ordenar y la función de flecha indica el modo en el que se ordenará que es mediante aleatorios. Por tanto, si ordenamos de forma aleatoria lo que estamos haciendo es desordenar.

Una vez desordenada el array lista49, creamos otro array denominado lista que toma del anterior únicamente los seis primeros elementos. Esto se hace con slice.

Finalmente se ordena la lista son sort, recurriendo al clásico método de ordenación de números a-b ya que si esto no se hace, se ordenará como si los números fueran un string y no quedaría correctamente ordenado.

 'use strict'  
 $(function(){  
  $("button").click(function(){  
   var lista49=Array.from({length: 49}, (v,k) => k+1);  
   lista49 = lista49.sort(() => Math.random()-0.5);  
   var lista=lista49.slice(0,6);  
   lista.sort((a,b)=>a-b);  
   $('div').html(lista.join(' '));  
  });  
 });  

Resumiendo lo que hacemos en esta versión es:
  • Creamos un array con los 49 números correlativos
  • Los desordenamos aleatoriamente
  • Elegimos los seis primeros
De esta forma nos garantizamos que los números elegidos son aleatorios sin repetición.

1 comentario: