Musical Script Solution

Viewed 35

guys

I am a beginner in ruby ​​and in my practices I thought of a musical script and there is a point that is making me sleepy: The moment I type Scale.major_by_note ('C') in irb everything is fine, but if I type Scale.major_by_note ('C #'), it doesn't work, for it to work I must put a "C # / Db", help me to make sure with both "C" and "C #" and "C # / Db", thank you! below is the script:

class Scale
  NATURAL = %w[C D E F G A B].freeze
  ACCIDENT = %w[C# Db D# Eb F# Gb G# Ab A# Bb].freeze
  CHROMATIC = %w[C C#/Db D D#/Eb E F F#/Gb G G# A A#/Bb B].freeze
  SCALE_MAJOR_PATTERN = [0, 2, 4, 5, 7, 9, 11, 12].freeze # T T st T T T st
  SCALE_MINOR_PATTERN = [0, 2, 3, 5, 7, 8, 10, 12].freeze # T st T T st T T

  def self.show_all_scales(note)
    major = Scale.major_by_note(note)
    minor = Scale.minor_by_note(note)
    all = { major: major, minor: minor}
    
  end

  def self.major_by_note(note)
    major_note_index = CHROMATIC.index(note)

    SCALE_MAJOR_PATTERN.map do |major_interval| # Interação
      major_scale_note_index = major_note_index + major_interval

      if major_scale_note_index <= (CHROMATIC.length - 1)
        CHROMATIC[major_scale_note_index]

      else
        reseted_major_scale_note_index = major_scale_note_index - CHROMATIC.length
        CHROMATIC[reseted_major_scale_note_index]
      end
    end
  end

  def self.minor_by_note(note)
    minor_note_index = CHROMATIC.index(note)

    SCALE_MINOR_PATTERN.map do |minor_interval|
      minor_scale_note_index = minor_note_index + minor_interval

      if minor_scale_note_index <= (CHROMATIC.length - 1)
        CHROMATIC[minor_scale_note_index]
      else
        reseted_minor_scale_note_index = minor_scale_note_index - CHROMATIC.length
        CHROMATIC[reseted_minor_scale_note_index]
      end
    end
  end
end```
1 Answers

When you type

%w[C C#/Db D D#/Eb E F F#/Gb G G# A A#/Bb B]

Ruby is turning this into an Array of Strings:

["C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#", "A", "A#/Bb", "B"]

Now while you know C# and Db are the same note, Ruby doesn't. It thinks the note in this case is called C#/Db. When it tries to find CHROMATIC.index("C#") it is returning nil because there is no C# in the Array.

A solution could be to write it like this:

  CHROMATIC = %w[C C# D D# E F F# G G# A A# B].freeze
  CHROMATIC_PAIR_MAP = {
    "Db" => "C#",
    "Eb" => "D#",
    "Gb" => "F#",
    "Ab" => "B#",
  }

  ...

  def self.index_of_note(note)
    CHROMATIC.index(note) ||
      CHROMATIC.index(CHROMATIC_PAIR_MAP[note])
  end

  def self.major_by_note(note)
    major_note_index = index_of_note(note)

Here I am making a new helper method to get the index of the note by either getting it straight from CHROMATIC array, or looking up a the note key in the CHROMATIC_PAR_MAP Hash. It will only perform the lookup in the Hash if CHROMATIC.index(note) returns nil. This is what I get in the console (irb):

irb(main):191:0> Scale.major_by_note("C#")
=> ["C#", "D#", "F", "F#", "G#", "A#", "C", "C#"]
irb(main):192:0> Scale.major_by_note("Db")
=> ["C#", "D#", "F", "F#", "G#", "A#", "C", "C#"]
irb(main):193:0> Scale.major_by_note("D#")=> ["D#", "F", "G", "G#", "A#", "C", "D", "D#"]
irb(main):194:0> Scale.major_by_note("Eb")=> ["D#", "F", "G", "G#", "A#", "C", "D", "D#"]

The full new class:

class Scale
  NATURAL = %w[C D E F G A B].freeze
  ACCIDENT = %w[C# Db D# Eb F# Gb G# Ab A# Bb].freeze
  CHROMATIC = %w[C C# D D# E F F# G G# A A# B].freeze
  SCALE_MAJOR_PATTERN = [0, 2, 4, 5, 7, 9, 11, 12].freeze # T T st T T T st
  SCALE_MINOR_PATTERN = [0, 2, 3, 5, 7, 8, 10, 12].freeze # T st T T st T T

  CHROMATIC_PAIR_MAP = {
    "Db" => "C#",
    "Eb" => "D#",
    "Gb" => "F#",
    "Ab" => "B#",
  }

  def self.show_all_scales(note)
    major = Scale.major_by_note(note)
    minor = Scale.minor_by_note(note)
    all = { major: major, minor: minor}
  end

  def self.major_by_note(note)
    major_note_index = index_of_note(note)
    SCALE_MAJOR_PATTERN.map do |major_interval| # Interação
      major_scale_note_index = major_note_index + major_interval

      if major_scale_note_index <= (CHROMATIC.length - 1)
        CHROMATIC[major_scale_note_index]

      else
        reseted_major_scale_note_index = major_scale_note_index - CHROMATIC.length
        CHROMATIC[reseted_major_scale_note_index]
      end
    end
  end

  def self.minor_by_note(note)
    minor_note_index = CHROMATIC.index(note)

    SCALE_MINOR_PATTERN.map do |minor_interval|
      minor_scale_note_index = minor_note_index + minor_interval

      if minor_scale_note_index <= (CHROMATIC.length - 1)
        CHROMATIC[minor_scale_note_index]
      else
        reseted_minor_scale_note_index = minor_scale_note_index - CHROMATIC.length
        CHROMATIC[reseted_minor_scale_note_index]
      end
    end
  end

  def self.index_of_note(note)
    CHROMATIC.index(note) ||
      CHROMATIC.index(CHROMATIC_PAIR_MAP[note])
  end
end
Related