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.
Tabla de contenidos
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:
- Los párrafos constan de frases y fragmentos.
- Los párrafos estén construidos con un número aleatorio de frases y fragmentos.
- Se debería introducir un fragmento tras un número aleatorio de frases.
- Las frases y fragmentos deben empezar en minúscula excepto si se trata del primero o si es un nombre propio.
- 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.