In this post I want to show you how to create a random text generator from scratch. I have chosen the lyrics of Romeo Santos, on the one hand because I am a fan of his music, and on the other because this artist inserts small fragments that are repeated in his songs. Latin singers like Romeo Santos with his “So nasty”, Daddy Yankee’s “DY” or Elvis Crespo’s “Cóhelo” could serve as guinea pigs for our experiment.
Table of Contents
Let’s do it
What we need first of all is a list of phrases and a list of snippets:
The phrases would be remarkable phrases from the lyrics of Romeo Santos como “Son las 5 de la mañana y yo no he dormido nada”.
The fragments would be those expressions that are repeated between songs, such as the previously mentioned “So nasty” or “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" ];
Since what we want is to randomly obtain both phrases and fragments, we create a small class dedicated exactly to that.
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
gets a random element from any array we give to it.
Generating paragraphs
The next step is to generate paragraphs, and we must start taking into account the following considerations:
- Paragraphs consist of sentences and fragments.
- Paragraphs are made up of a random number of sentences and fragments.
- A fragment should be inserted after a random number of sentences.
- Phrases and fragments must begin in lowercase except when it is the first one or in the case of a proper name.
- We must join sentences and fragments with commas, spaces and periods.
To achieve this, we will have to cook the following recipes:
Paragraphs constructed with a random number of sentences and fragments
Ingredients
We need a couple of parameters: the minimum and maximum of elements per paragraph (understanding both sentences and fragments as elements).
DIRECTIONS FOR USE
We create two functions:
- One that returns a random number when given a minimum and maximum.
- Another one that uses the previously defined function to return the number of elements per paragraph, using the minimum and maximum elements per paragraph parameters.
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); } }
Inserting a fragment after a random number of sentences
Ingredients
- A couple of minimum and maximum parameters (this time of phrases after which a fragment should go).
- The previously cooked function: randomizer.
DIRECTIONS FOR USE
We create the function that obtains a random number between the minimum and maximum parameters defined in this section (as it was done in the previous one).
When generating this paragraph, we will control whether the following element must be a fragment or phrase based on the random number generated:
- If the number of phrases entered is equal to that number, the next element will be a fragment and we will recalculate the random number in addition to resetting the phrase counter.
- If it is not, it is still a phrase, and we count it for the next iteration.
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>`; } }
Phrases and fragments must start in lowercase
We can create a StringHelper
class that is valid to carry out the conversion of text strings. In the case of converting the texts to lowercase we must be careful that it is not a proper name. In this case, it is only given for the fragment “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); } }
Having the paragraph
variable that is the result text string to which more text is concatenating, within a loop whose index is the variable i
, and the variable romeoText
containing the phrase or fragment:
paragraph += 1 === i ? StringHelper.firstLetterToUpperCase(romeoText) : StringHelper.firstLetterToLowerCase(romeoText);
Joining sentences and fragments by commas, spaces, and periods
ingredients
- The resulting text string within the iteration where text is concatenated:
paragraph
- The fragment or phrase of Romeo:
romeoText
- The variable that contains the number of elements per paragraph:
elementsPerParagraph
- The index of the current iteration:
i
DIRECTIONS FOR USE
This is the easiest part with the exception that, if a fragment or phrase ends with an exclamation point or punctuation, it does not need to be followed by a comma or period.
We have the text string variable result paragraph
again: if the index is equal to the number of elements per paragraph (that is, it is already the last element) a period is concatenated. If not, a comma.
if (!romeoText.endsWith('?') && !romeoText.endsWith('?') ) { paragraph+= i === elementsPerParagraph ? '.' : ','; }
If the phrase or fragment is not the last element, a space is added to it:
paragraph+= i === elementsPerParagraph ? '': ' ';
Serving the dishes
We return in the function that generates the paragraph the concatenation of sentences and fragments wrapped in a paragraph element.
In short, our dishes would look like this on the table:
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>`; } }
Inserting the content
We create the final class to which we add the element parameters where the content will be injected, and the number of paragraphs to generate. In addition, the method to obtain the generated paragraphs.
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>`); } } }
And we can use it the following way:
let romeoIpsum = new RomeoIpsum(document.querySelector('.romeo'), 3); document.querySelector('button').addEventListener('click', () => romeoIpsum.render());
The final result:
See the Pen
Romeo Sanctum by Carlos Sanchidrián (@Katenmi)
on CodePen.