Desarrollo

Creación de un generador de textos aleatorios en Javascript con letras de Romeo Santos

En este post os quiero enseñar cómo crear un generador de textos aleatorios desde cero. He elegido las letras de Romeo Santos, por una parte porque soy fan y por otro porque este artista inserta pequeños fragmentos que se repiten en sus canciones. Tanto Romeo Santos con su «So nasty», como otros cantantes latinos como el «diuai» (DY) de Daddy Yankee o el «Cóhelo» de Elvis Crespo, podrían servirnos como conejillos para nuestro experimento.

Manos a la obra

Lo que necesitamos antes de nada es una lista de frases y una lista de fragmentos:

Las frases serían frases destacables de las letras de Romeo Santos como «Son las 5 de la mañana y yo no he dormido nada».

Los fragmentos serían esas expresiones que se repiten entre canciones, como el anteriormente mencionado «So nasty» o «Aventura».

const romeoSentence = [
  "Como Superman",
  "Trae la toalla porque te vas a mojar",
  "Una aventura es más divertida si huele a peligro",
  "No te hagas la tonta, eso muy bien ya lo sabías",
  "¿Y si te invito a una copa y me acerco a tu boca...?",
  "Son las 5 de la mañana y yo no he dormido nada",
  "Si te parece prudente esta propuesta indecente",
  "Que les perdone Dios, yo no lo pienso hacer",
  "Estoy planeando un secuestro... para robarme tu amor",
  "La baticueva: nuestro nidito de amor",
  "Ya me han informado que tu novio es un insípido aburrido"
];


const romeoFragment = [
  "Ay chí chí",
  "Llora guitarra... llora",
  "Feels good to be king",
  "El chico de las poesías",
  "¿Te gusta mi bachata mijita?",
  "Yes sir",
  "The king",
  "Maldito sentimiento",
  "Aventura",
  "So nasty",
  "Me quiero morir",
  "Tu grupo favorito mami",
  "You won't forget Romeo",
  "Gostoso",
  "Escucha las palabras"
];

Como lo que queremos es obtener de forma aleatoria tanto frases como fragmentos, creamos una pequeña clase que se dedique justo a eso.

class RandomTexts
{
  static getElementFromStringCollection(stringCollection)
  {
    let max = stringCollection.length;
    let index = Math.floor(Math.random() * max)
    return stringCollection[index];
  }
  static getFragment()
  {
      return RandomTexts.getElementFromStringCollection(romeoFragment);
  }
  
  static getSentence()
  {
      return RandomTexts.getElementFromStringCollection(romeoSentence);
  }
}

getElementFromStringCollection obtiene un elemento aleatorio del array que se le pase.

Generar párrafos

A continuación, necesitamos generar párrafos y partimos de que las siguientes consideraciones:

  1. Los párrafos constan de frases y fragmentos.
  2. Los párrafos estén construidos con un número aleatorio de frases y fragmentos.
  3. Se debería introducir un fragmento tras un número aleatorio de frases.
  4. Las frases y fragmentos deben empezar en minúscula excepto si se trata del primero o si es un nombre propio.
  5. Unir frases y fragmentos por comas, espacios, y puntos.

Para ello tendremos que cocinar las siguientes recetas:

Párrafos construidos con un número aleatorio de frases y fragmentos

Ingredientes

Necesitamos un par de parámetros: el mínimo y máximo de elementos por párrafo (entendiendo como elementos tanto las frases como fragmentos).

modo de empleo

Creamos dos funciones:

  • Una que, dado un mínimo y máximo, devuelva un número aleatorio.
  • Otra que use la función anteriormente definida para devolver la cantidad de elementos por párrafo, usando los parámetros de mínimo y máximo de elementos por párrafo.
class ParagraphBuilder
{
  static get minElementsPerParagraph() { return 3 }
  static get maxElementsPerParagraph() { return 8 }
  
  static randomizer(min, max)
  {
     return Math.floor(Math.random() * (max - min) + min);
  }
  
  static elementsPerParagraph()
  {
    return ParagraphBuilder.randomizer(ParagraphBuilder.minElementsPerParagraph, ParagraphBuilder.maxElementsPerParagraph);
  }
}

Introducir un fragmento tras un número aleatorio de frases

Ingredientes

  • Un par de parámetros de mínimo y máximo (esta vez de frases tras las cuales debería ir un fragmento).
  • La función cocinada anteriormente: randomizer.

Modo de empleo

Creamos la función que obtenga un número aleatorio entre los parámetros de mínimo y máximo definidos en esta sección (igual que se hizo en la sección anterior).

En la generación de párrafo controlaremos si el siguiente elemento debe ser un fragmento o frase en función del número aleatorio generado:

  • Si el número de frases que se han introducido es igual a dicho número, el siguiente elemento es un fragmento y volvemos a calcular el número aleatorio además de resetear el contador de frases.
  • Si no lo es, sigue siendo una frase, y la contamos para la próxima iteración.
class ParagraphBuilder
{
  static get fragmentBetweenMinSentences() { return 1 }
  static get fragmentBetweenMaxSentences() { return 2 }
  
  static randomizer(min, max)
  {
     return Math.floor(Math.random() * (max - min) + min);
  }

  static fragmentBetweenNumberOfSentences()
  {
    return ParagraphBuilder.randomizer(ParagraphBuilder.fragmentBetweenMinSentences, ParagraphBuilder.fragmentBetweenMaxSentences);
  }
  
  static generateParagraph()
  {
    // Contador de frases
    let sentencesSinceLastFragment = 0;

    // Número de frases tras las cuales se debería introducir un fragmento
    let fragmentBetweenNumberOfSentences = ParagraphBuilder.fragmentBetweenNumberOfSentences();
    
    // Número de elementos por párrafo (Receta anterior)
    let elementsPerParagraph = ParagraphBuilder.elementsPerParagraph();
    
    for (var i = 1; i <= elementsPerParagraph; i++) {
      let romeoText = '';
      
      if(sentencesSinceLastFragment === fragmentBetweenNumberOfSentences) {
        sentencesSinceLastFragment = 0;
        fragmentBetweenNumberOfSentences = ParagraphBuilder.fragmentBetweenNumberOfSentences();
        romeoText = RandomTexts.getFragment();
      } else {
        sentencesSinceLastFragment++;
        romeoText = RandomTexts.getSentence();
      }
    }
    return `<p>${paragraph}</p>`;
  }
}

Las frases y fragmentos deben empezar en minúscula

Nos podemos crear una clase StringHelper que valga para realizar la conversión de cadenas de texto, en el caso de pasar los textos a minúsculas debemos tener cuidado de que no se trate de un nombre propio, en este caso sólo se da para el fragmento «Aventura».

class StringHelper
{
  static firstLetterToUpperCase(string)
  {
      return string.charAt(0).toUpperCase() + string.slice(1);
  }
  
  static firstLetterToLowerCase(string) {
    return string === 'Aventura'
      ? string
      : string.charAt(0).toLowerCase() + string.slice(1);
  }
}

Teniendo la variable paragraph que es la cadena de texto resultado a la que se le va concatenando más texto, dentro de un bucle cuyo índice es la variable i, y que la variable romeoText contiene la frase o fragmento:

paragraph += 1 === i
  ? StringHelper.firstLetterToUpperCase(romeoText)
  : StringHelper.firstLetterToLowerCase(romeoText);

Unir frases y fragmentos por comas, espacios, y puntos

ingredientes

  • La cadena de texto resultante dentro de la iteración donde se le va concatenando texto: paragraph
  • El fragmento o frase de Romeo: romeoText
  • La variable que contiene el número de elementos por párrafo: elementsPerParagraph
  • El indice de la iteración actual: i

Modo de empleo

En teoría es lo más fácil con la salvedad de que si un fragmento o frase acaba por signo de exclamación o puntuación no necesita que le siga una coma o punto.

Volvemos a tener la variable de cadena de texto resultado paragraph, si el indice es igual al número de elementos por párrafo (es decir, es ya el último elemento) se le concatena un punto, si no, una coma.

if (!romeoText.endsWith('?') && !romeoText.endsWith('?') ) {
   paragraph+= i === elementsPerParagraph ? '.' : ',';
}

A mayores si la frase o fragmento no es el último elemento, se le agrega un espacio:

paragraph+= i === elementsPerParagraph ? '': ' ';

Presentación en la mesa

Devolvemos en la función que genera el párrafo la concatenación de frases y fragmentos envueltos en un elemento de párrafo.

En  definitiva,los platos se verían así en nuestra mesa:

class ParagraphBuilder
{
  static get minElementsPerParagraph() { return 3 }
  static get maxElementsPerParagraph() { return 8 }
  static get fragmentBetweenMinSentences() { return 1 }
  static get fragmentBetweenMaxSentences() { return 2 }
  
  static randomizer(min, max)
  {
     return Math.floor(Math.random() * (max - min) + min);
  }
  
  static elementsPerParagraph()
  {
    return ParagraphBuilder.randomizer(ParagraphBuilder.minElementsPerParagraph, ParagraphBuilder.maxElementsPerParagraph);
  }
  
  static fragmentBetweenNumberOfSentences()
  {
    return ParagraphBuilder.randomizer(ParagraphBuilder.fragmentBetweenMinSentences, ParagraphBuilder.fragmentBetweenMaxSentences);
  }
  
  static generateParagraph()
  {
    let paragraph = '';
    let sentencesSinceLastFragment = 0;
    let fragmentBetweenNumberOfSentences = ParagraphBuilder.fragmentBetweenNumberOfSentences();
    let elementsPerParagraph = ParagraphBuilder.elementsPerParagraph();
    
    for (var i = 1; i <= elementsPerParagraph; i++) {
      let romeoText = '';
      
      if(sentencesSinceLastFragment === fragmentBetweenNumberOfSentences) {
        sentencesSinceLastFragment = 0;
        fragmentBetweenNumberOfSentences = ParagraphBuilder.fragmentBetweenNumberOfSentences();
        romeoText = RandomTexts.getFragment();
      } else {
        sentencesSinceLastFragment++;
        romeoText = RandomTexts.getSentence();
      }
    
      paragraph += 1 === i
        ? StringHelper.firstLetterToUpperCase(romeoText)
        : StringHelper.firstLetterToLowerCase(romeoText);
   
      if (!romeoText.endsWith('?') && !romeoText.endsWith('?') ) {
        paragraph+= i === elementsPerParagraph ? '.' : ',';
      }
      paragraph+= i === elementsPerParagraph ? '': ' ';
    }
    return `<p>${paragraph}</p>`;
  }
}

Insertamos el contenido

Creamos la clase final a la que se le pasarán los parámetros de elemento donde se va a inyectar el contenido, y número de párrafos a generar. A mayores un método para obtener los párrafos generados.

class RomeoIpsum
{   
  constructor($element, numberOfParagraphs = 3)
  {
    this.$container = $element;
    this.numberOfParagraphs = numberOfParagraphs;
  }
  
  render()
  {
    this.$container.innerHTML = '';
    for (var i = 0; i < this.numberOfParagraphs; i++ ) {
      this.$container.insertAdjacentHTML('beforeend', `<p>${ParagraphBuilder.generateParagraph()}</p>`);
    }
  }
}

Y la podemos usar del siguiente modo:

let romeoIpsum = new RomeoIpsum(document.querySelector('.romeo'), 3);
document.querySelector('button').addEventListener('click', () => romeoIpsum.render());

El resultado final:

See the Pen
Romeo Sanctum
by Carlos Sanchidrián (@Katenmi)
on CodePen.

Carlos Sanchidrián

Full Stack Developer

Además de indescriptible, soy un amante del desarrollo. Necesito 25 horas al día para poder dedicarle tiempo al "polígono" amoroso que tengo con el piano, el longboard, el snowboard, la domótica, el aermodelismo, la aviación y el teclado. ¿Polifacético dirías?

¿Hablamos?
Sólo se admiten ficheros PDF, Word y OpenOffice de 2MB como máximo.

PARA TU TRANQUILIDAD
Premium Leads S. L. solo utilizará tus datos para dar respuesta tu consulta y nunca los cederá a terceros. Para ejercer tus derechos, consulta la política de privacidad.