2

Currently I am replacing just a character with something different, as it is a music sheet that needs to be altered the character might differ. Therefore I created a JavaScript code which will replace all these letters if on the sheet.

Here the problem occurs, If I have character A which needs to be replaced with C and later on in the same JavaScript code C needs to be replaced with D. Therefore A changes to D instead of C. I need every character replaced once per action instead of comparing the replaced text with other possibilities.

Here is my code so far:

function Dkoord() {
  $('[id^=harm] > text:nth-child(1) > tspan:nth-child(1)').text(function(i, text) {
    return text.replace('A', 'B').replace('A#', 'C').replace('Bb', 'C')
               .replace('B', 'C#').replace('C', 'D').replace('C#', 'D#')
               .replace('Db', 'Eb').replace('D', 'E').replace('D#', 'F')
               .replace('Eb', 'F').replace('F', 'G').replace('F#', 'G#')
               .replace('Gb', 'Ab').replace('G', 'A').replace('G#', 'A#')
               .replace('Ab', 'Bb');
});
}

As you can see C is changing to D but later on changing the D to E, I just want a character to be compared once. How do I do this.

4
  • Store the changes to a temporary variable and return that once done instead. Commented Sep 29, 2016 at 8:26
  • firstly replace C to D then A to C will solve your problem. Commented Sep 29, 2016 at 8:27
  • @Chris, can you give me an example on how to temporary variable a function and then execute it? thanks Commented Sep 29, 2016 at 8:36
  • Not what you're asking, but musically speaking you can't transpose notes/chords like that, you have to take into account what the new key is. (Because, e.g., if transposing up from B major to C# major a D# note would have to become E# rather than F.) Commented Sep 29, 2016 at 8:36

3 Answers 3

5

Use one regular expression to match what you want to replace, something like this:

/[A-G](b|#)?/g

That is, match any letters from A-G, optionally followed by b or #.

Then use .replace() with a callback to lookup what was matched and return the appropriate replacement.

function Dkoord() {
  var replacements = {
      'A': 'B', 'A#': 'C', 'Bb': 'C', 'B': 'C#', 'C': 'D', 'C#': 'D#',
      'Db': 'Eb', 'D': 'E', 'D#': 'F', 'Eb': 'F', 'F': 'G', 'F#': 'G#',
      'Gb': 'Ab', 'G': 'A', 'G#': 'A#', 'Ab': 'Bb'
  };

  $('[id^=harm] > text:nth-child(1) > tspan:nth-child(1)').text(function(i, text) {
    return text.replace(/[A-G](b|#)?/g, function(m) { return replacements[m]; });
  });
}

Expand and run the following snippet for a simplified demo:

function Dkoord(text) {
  var replacements = {
      'A': 'B', 'A#': 'C', 'Bb': 'C', 'B': 'C#', 'C': 'D', 'C#': 'D#',
 	  'Db': 'Eb', 'D': 'E', 'D#': 'F', 'Eb': 'F', 'F': 'G', 'F#': 'G#',
	  'Gb': 'Ab', 'G': 'A', 'G#': 'A#', 'Ab': 'Bb'
  };
    	  
  return text.replace(/[A-G](b|#)?/g, function(m) { return replacements[m]; });
}

console.log(Dkoord("A# B C D/F# G A"))

Note that using a lookup object like that also has the advantage that you could extend it to have more than one object to allow for transposing into different keys. For example, if transposing from B major to C# you should change D# into E#, but if transposing from F# major to Ab you should change D# into F.

Sign up to request clarification or add additional context in comments.

6 Comments

This is brilliant, although you forgot a couple of ending ), but the code worked perfectly for me. Thanks a bunch.. function Dkoord() { var replacements = { 'A': 'B', 'A#': 'C', 'Bb': 'C', 'B': 'C#', 'C': 'D', 'C#': 'D#', 'Db': 'Eb', 'D': 'E', 'D#': 'F', 'Eb': 'F', 'F': 'G', 'F#': 'G#', 'Gb': 'Ab', 'G': 'A', 'G#': 'A#', 'Ab': 'Bb'}; $('[id^=harm] > text:nth-child(1) > tspan:nth-child(1)').text(function(i, text) { return text.replace(/[A-G](b|#)?/, function(m) { return replacements[m]; }); }); }
Definitely the best approach here. Nice.
Sorry about the missing ) - I think I've now edited them back in. (In my original answer I started with the simplified version as in my demo so that I could test it, and then I messed up on putting your jQuery bit back in.)
P.S. Thanks @Chris. And Henk - Note that my replacement map was adapted directly from the code in your question, but I just noticed that you missed some possible starting notes, B#, E, and E#. Better double check that sort of thing in your real project.
Thanks @nnnnnn, I have just one more question: Now one of my lines is Cmaj7/A# The C is correctly replaced by D but the A# isn't changing at all. How can I make sure everything behind / is also being replaced? thanks again for the help.
|
2

One easy way of doing this would be to replace the initial value with a temporary one and then replacing the temporary variable again.

For example:

function Dkoord() {
  $('[id^=harm] > text:nth-child(1) > tspan:nth-child(1)').text(function(i, text) {
    return text.replace('A', 'b').replace('A#', 'c').replace('Bb', 'c')
               .replace('B', 'c#').replace('C', 'd').replace('C#', 'd#')
               .replace('Db', 'e_').replace('D', 'e').replace('D#', 'f')
               .replace('Eb', 'f').replace('F', 'g').replace('F#', 'g#')
               .replace('Gb', 'a_').replace('G', 'a').replace('G#', 'a#')
               .replace('Ab', 'b_').toUpperCase().replace(/[_]/g, "b");
  });
}

This will replace the text you want with the "correct" value in lowercase, this will prevent the next pipe from replacing the already replaced value.

Once done, you can go through your string and simply set everything back to uppercase. So just add this after your last replace.

.toUpperCase().replace(/[_]/g, "b");

Obviously, you can use any character(s) you wish, as long as it is unique.

Demo

$("#btn").on("click", function() {
  $("#demo").text(function(i, text) {
    return text.replace('A', 'b').replace('A#', 'c').replace('Bb', 'c')
      .replace('B', 'c#').replace('C', 'd').replace('C#', 'd#')
      .replace('Db', 'e_').replace('D', 'e').replace('D#', 'f')
      .replace('Eb', 'f').replace('F', 'g').replace('F#', 'g#')
      .replace('Gb', 'a_').replace('G', 'a').replace('G#', 'a#')
      .replace('Ab', 'b_').toUpperCase().replace(/[_]/g, "b");
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b id="demo">AC#Eb</b>
<button id="btn">Convert</button>

Comments

0

Try something like this

 function Dkoord() {

     $('[id^=harm] > text:nth-child(1) > tspan:nth-child(1)').text(function(i, text) {

      return text.replace('Ab', 'Bb').replace('G#', 'A#').replace('G', 'A')
            .replace('Gb', 'Ab').replace('F#', 'G#').replace('F', 'G')
            .replace('Eb', 'F').replace('D#', 'F').replace('D', 'E')
            .replace('Db', 'Eb').replace('C#', 'D#').replace('C', 'D')
            .replace('B', 'C#').replace('Bb', 'C').replace('A#', 'C')
            .replace('A', 'B')
 });
 }

1 Comment

Well this won't fix my problem, the C to D and D to E was just an example. The music sheet might as well start with Ab to Bb and is later changed from Bb to C which I don't want as well. Everything must be checked once, if checked execute replacement and don't check it again in same function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.