Alberto Moneti
Alberto Moneti

Reputation: 89

Check if all words from text line match RegEx elements (guitar chords)

I want to achieve the following:

1) Check if every word (in this case guitar chord) from a text line is contained in RegEx. All words/characters must match.

2) If yes, I want every single word from that line to become a link, pointing to an URL customized by the word itself.

I.E. The following line:

Am   Bb  C# Dadd9
Song lyrics here

should become this:

<a href="example.com/Am">Am</a>   <a href="example.com/Bb">Bb</a>  <a href="example.com/C#">C#</a> <a href="example.com/Dadd9">Dadd9</a>
Song lyrics here

My RegEx (notes and symbols forming each chord):

var lineRegex = /^\s*(?:(A|B|C|D|E|F|G|A#|C#|D#|F#|G#|Ab|Bb|Db|Eb|Gb){1}(?:m|\+|\-|aug|dim|add|b|#|1|2|3|4|5|6|7|8|9|0|\/)*\s*)+$/;

Here's what I've com up to for step 2:

var text = $('.input').val();   //get text
var lines = text.split('\n');   //split text into lines
var chords = lines.split(' ');  //split lines into chords

var link = "http://www.example.com/";
      $.each(chords, function(index, value) {   //append link to chord
        $('.output').append('<a href="' + link + value + '">' + value + '</a> ');
      });

I can't achieve step 1. How do I check if a text line contains only notes and symbols from RegEx?

Upvotes: 1

Views: 1862

Answers (1)

ctwheels
ctwheels

Reputation: 22817

Answer

Brief

I have no idea what chords are valid/invalid on a guitar, and since you haven't specified that information (in clear text - without implemented regex), and we aren't certain that your regex works, I'm basing my answer off of this random chart I found (first result on Google images for list of all guitar chords).

enter image description here


Regex

Sample 1

See this regex in use here

Based on this chart, I've created the following regex

(?(DEFINE)
  (?<chord_A>A(?:b(?:[679+]|sus)?|m[67]?|[689]|maj7|dim|\+|sus)?)
  (?<chord_B>B(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)
  (?<chord_C>C(?:[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim))?)
  (?<chord_D>D(?:b(?:[679+]|maj7|sus)?|[679]|m[67]?|maj7|dim|\+|sus)?)
  (?<chord_E>E(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)
  (?<chord_F>F(?:[679+]|m[67]?|maj7|dim|sus|\#(?:[79]|m[67]|dim)?)?)
  (?<chord_G>G(?:b(?:maj7|sus|[6+])?|[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim))?)
  (?<chords>(?&chord_A)|(?&chord_B)|(?&chord_C)|(?&chord_D)|(?&chord_E)|(?&chord_F))
)
(?<link>\b(?&chords)(?=\s|$))

In this regex, I've defined groups according to chords' starting letter (e.g. A).

This regex works by defining all possible instances of each chord (in each group chord_X where X represents the chord, i.e. C). The defined group chords is a comparison between each chord (e.g. Chord A or Chord B or ...). The final regex uses (?<link>\b(?&chords)(?=\s|$)). This regex ensures that a word boundary character exists before the chord (each begins with a letter so this will not fail), and is followed by either a space or the end of the string $ (\b cannot be used here since some notes include word boundary characters - such as #, thus, to properly catch these instances, we use spaces or EOF).

Sample 2

See this regex in use here

Note that since C# doesn't exist in the image (unless I'm blind), it won't catch C# from your input. You can change this behaviour by using the following regex instead (which just adds a ? at the end of \#(?:m[67]?|dim) in the chord_C defined group.

(?(DEFINE)
  (?<chord_A>A(?:b(?:[679+]|sus)?|m[67]?|[689]|maj7|dim|\+|sus)?)
  (?<chord_B>B(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)
  (?<chord_C>C(?:[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim)?)?)
  (?<chord_D>D(?:b(?:[679+]|maj7|sus)?|[679]|m[67]?|maj7|dim|\+|sus)?)
  (?<chord_E>E(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)
  (?<chord_F>F(?:[679+]|m[67]?|maj7|dim|sus|\#(?:[79]|m[67]|dim)?)?)
  (?<chord_G>G(?:b(?:maj7|sus|[6+])?|[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim))?)
  (?<chords>(?&chord_A)|(?&chord_B)|(?&chord_C)|(?&chord_D)|(?&chord_E)|(?&chord_F))
)
(?<link>\b(?&chords)(?=\s|$))

Substitution

<a href="example.com/${link}">${link}</a>

The substitution makes reference to the named capture group from our regex link.


Results

Input

Am   Bb  C# Dadd9
Song lyrics here

Sample 1 - Output

<a href="example.com/Am">Am</a>   <a href="example.com/Bb">Bb</a>  C# Dadd9
Song lyrics here

Sample 2 - Output

<a href="example.com/Am">Am</a>   <a href="example.com/Bb">Bb</a>  <a href="example.com/C#">C#</a> Dadd9
Song lyrics here

Edit

Since javascript doesn't use PCRE regex, you can't use named capture groups as the above suggests, so, unfortunately, we're going to have to use a beautiful long and unreadable regex as follows...

(\b(?:(?:A(?:b(?:[679+]|sus)?|m[67]?|[689]|maj7|dim|\+|sus)?)|(?:B(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)|(?:C(?:[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim)?)?)|(?:D(?:b(?:[679+]|maj7|sus)?|[679]|m[67]?|maj7|dim|\+|sus)?)|(?:E(?:b(?:[679+]|sus|m[67]?|maj7|dim)?|m[67]?|[679+]|maj7|dim|sus)?)|(?:F(?:[679+]|m[67]?|maj7|dim|sus|\#(?:[79]|m[67]|dim)?)?)|(?:G(?:b(?:maj7|sus|[6+])?|[679+]|m[67]?|maj7|dim|sus|\#(?:m[67]?|dim))?))(?=\s|$))

With <a href="example.com/$1">$1</a> as the substitution.

Upvotes: 2

Related Questions