Creation of a random text generator in Javascript with lyrics by Romeo Santos

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.

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:

  1. Paragraphs consist of sentences and fragments.
  2. Paragraphs are made up of a random number of sentences and fragments.
  3. A fragment should be inserted after a random number of sentences.
  4. Phrases and fragments must begin in lowercase except when it is the first one or in the case of a proper name.
  5. 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.

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?

Wanna talk?
Only PDF, Word and OpenOffice files. Size: 2MB max.

FOR YOUR INTEREST
Premium Leads S. L. will only use your data to answer your request and they will never be given to third parties. To exercise your rights, check our privacy policy.

Contacta con nosotros a través de WhatsApp