skip to Main Content

I’m attempting to create a javascript function that will take an input, count the characters before a piece of content surrounded by square brackets and output the content above that line of the input with correct spacing based on the amount of characters leading up to that content. This project is to create music charts while being able to write the chords inline with the lyrics.

Example input:

This is a [G]lyric to a [C]song
I could [D]sing it all day [Em]long

Desired output:

          G           C
This is a lyrics to a song
        D               Em
I could sing it all day long

I’m not incredibly experienced with Javascript, but I know the basics. This project is just a little over my head. I’ve attached the code I have so far.

function processText(input) {
  const lines = input.split("n");
  let output = "";

  for (let line of lines) {
    let startIndex = line.indexOf("[");
    let endIndex = line.indexOf("]", startIndex);

    let lastIndex = 0;
    let lineOutput = "";

    while (startIndex !== -1 && endIndex !== -1) {
      const padding = startIndex - lastIndex;
      const content = line.substring(startIndex + 1, endIndex);

      lineOutput += " ".repeat(padding);
      lineOutput += content + " ";

      lastIndex = endIndex + 1;

      line = line.substring(endIndex + 1);
      startIndex = line.indexOf("[");
      endIndex = line.indexOf("]", startIndex);
    }

    lineOutput += line.substring(lastIndex);
    output += lineOutput.trim() + "n";
  }

  return output;
}

const input = `This is a [G]lyric to a [C]song
  I could [D]sing it all day [Em]long`;

const output = processText(input);
console.log(output);

I’m afraid my limited experience with JS has me at a standstill currently.

The current output of the code is

$ node test.js
           G  C
           D     Em

Which actually wouldn’t be as problematic if the spacing was actually correct.. but I can’t seem to get that right..

3

Answers


  1. let newLyrics = `This is a [G]lyric to a [C]song
    I could [D]sing it all day [Em]long`;
    
    function processText(lyrics) {
        let lines = lyrics.split("n");
        
        lines.forEach((line) => {
            line += "n"
            
            console.log(line.replace(/b[a-zA-Z]+s+|[|]/g, (match) => {
                return " ".repeat(match.length)
            }));
            console.log(line);
        });
    }
    
    processText(newLyrics);

    reference

    🙂

    Login or Signup to reply.
  2. In order to solve problem to get desired output you need to first split the content into lines and then process each line .

    Here i created a processLine function which processes the line and splits the line into 2 parts the lyric string and string without those G ,C or EM .

    The logic is pretty simple . The processLine function will use regex to find the match and then create another string .

    To position the lyrics the algo finds the distance between the previous match and next match and append space using Array(space).join() method.

    Heres the complete implementation :

    const content = `This is a [G]lyric to a [C]song
    I could [D]sing it all day [Em]long`;
    
    
    const processLine = (/** @type {String}*/ line) => {
        const removeSquareBraces = (text) => {
            text = text.replace('[', ' ');
            text = text.replace(']', ' ');
            return text;
        }
        let firstLine = line, secondLine = '';
        const matches = firstLine.match(/[w+]/gm);
        let prevMatchIndex = 0;
        matches.forEach((match, i) => {
            const index = firstLine.indexOf(match);
            // finding index and padding the string 
            if (prevMatchIndex != 0) {
                secondLine += Array(index - prevMatchIndex).join(" ");
            } else {
                secondLine += Array(index - match.length).join(" ");
                prevMatchIndex = index;
            }
    
            secondLine += removeSquareBraces(match);
            firstLine = firstLine.replace(match, ""); // cleaning string
        });
        return [firstLine, secondLine];
    }
    
    
    
    content.split(
        "n"
    ).forEach(
        line => {
            const [line1, line2] = processLine(line); // destructuring 
            console.log(line2)
            console.log(line1)
        })
    
    
    Login or Signup to reply.
  3. You can so something like this:

    const input = `This is a [G]lyric to a [C]song
    I could [D]sing it all day [Em]long`;
    
    const output = input.split('n').reduce((a, c) => {
      const parts = c.split(/([[^]]+])/);
      const lines = parts.reduce((acc, curr) => {
        if (curr.startsWith('[') && curr.endsWith(']')) {
          const cleanNote = curr.replace(/[[]]/g, '');
          const offset = acc.lyrics.length - acc.notes.length + cleanNote.length;
          acc.notes += cleanNote.padStart(offset, ' ');
        } else {
          acc.lyrics += curr;
        }
        return acc;
      }, { notes: '', lyrics: ''});
      a.push(lines.notes);
      a.push(lines.lyrics);
      return a;
    }, []).join('n');
    
    console.log(output);

    What this does, is to first split the input into lines.
    Then, reduce those lines to generate an array of alternating notes and lyrics.

    How that is done is to split each line so it will separate it into an array of lyrics and notes (c.split(/([[^]]+])/)).

    That array is then reduced to an object that has as properties notes and lyrics which are strings.

    To calculate the offset, you can simply subtract the current length of the notes line from the current length of the lyrics line add add to that the length of the note (acc.lyrics.length - acc.notes.length + cleanNote.length).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search