Sam P
Sam P

Reputation: 117

Replacing letters in string with their numeric position in the alphabet in ruby

I'm trying to make a method that, given a string, replaces every letter with its position in the alphabet.

If anything in the text isn't a letter, I want to ignore it and not return it.

"a" = 1, "b" = 2, etc.

Example

alphabet_position("The sunset sets at twelve o' clock.")

Should return "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11" (as a string) I tried this, but it didn't work:

    def alphabet_position(text)
      alph = ("a".."z").to_a
      text = text.split(/./).map {|ch| if ch.in?(alph)
        ((alph.index[ch]).to_i+1).to_s
        else
          ""
        end
      }.join(" ").strip
    end

Thanks in advance!

Upvotes: 0

Views: 1675

Answers (5)

engineersmnky
engineersmnky

Reputation: 29383

First we can build the translation because Hash lookups are extremely fast:

# alternatively letter_to_number = ('a'..'z').each.with_index(1).to_h
letter_to_number = ('a'..'z').zip(1..26).to_h 
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, 
#    "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, 
#    "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, 
#    "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}

Then simply swap them out

# ruby >= 2.7 
text.downcase.each_char.filter_map {|c| letter_to_number[c] }.join(' ')
# ruby < 2.7 
text.downcase.each_char.map {|c| letter_to_number[c] }.compact.join(' ')
#=> "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11"

Upvotes: 4

Cary Swoveland
Cary Swoveland

Reputation: 110685

def convert(str)
  str.downcase.each_char.with_object('') do |c,s|
    n = c.ord
    if n.between?(97, 122)
      s << ' ' unless s.empty?
      s << (n-96).to_s
    end
  end
end
convert "Sunset was at twelve o'clock, somewhere, on some day"
  #=> "19 21 14 19 5 20 23 1 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11 19 15 13 5 23 8 5 18 5 15 14 19 15 13 5 4 1 25"

Lady and gentleman, start your engines!

def alphabet_position(text)
  text.downcase.split('').map do |letter|
    index = ('a'..'z').find_index(letter)
    index + 1 if index
  end.compact.join(' ')
end
def smnky_1_7(str)
  letter_to_number = ('a'..'z').zip(1..26).to_h
  str.downcase.each_char.filter_map {|c| letter_to_number[c] }.join(' ')
end
def smnky_pre_1_7(str)
  letter_to_number = ('a'..'z').zip(1..26).to_h
  str.downcase.each_char.map {|c| letter_to_number[c] }.compact.join(' ')
end
LETTER_TO_NUMBER = ('a'..'z').zip(1..26).to_h
def smnky_pre_w_constant(str)
  str.downcase.each_char.map {|c| LETTER_TO_NUMBER[c] }.compact.join(' ')
end
def convert_with_arr(str)
  str.downcase.each_char.with_object([]) do |c,arr|
    n = c.ord
    arr << n - 96 if n.between?(97, 122)
  end.join(' ')
end
str = "Sunset was at twelve o'clock, somewhere, on some day"
require 'benchmark'
def run(m, str)
  500.times { method(m).call(str) }
end
Benchmark.bm(19) do |x|
  x.report("Cary")                 { run(:convert, str) }
  x.report("Tatiana")              { run(:alphabet_position, str) }
  x.report("smnky 1.7")            { run(:smnky_1_7, str) }
  x.report("smnky pre 1.7")        { run(:smnky_1_7, str) }
  x.report("smnky pre w/constant") { run(:smnky_pre_w_constant, str) }
  x.report("Cary with arr")        { run(:convert_with_arr, str) }
end
                          user     system      total        real
Cary                  0.018610   0.000300   0.018910 (  0.019135)
Tatiana               0.067738   0.001138   0.068876 (  0.070317)
smnky 1.7             0.028659   0.001035   0.029694 (  0.030583)
smnky pre 1.7         0.032050   0.001662   0.033712 (  0.035089)
smnky pre w/constant  0.013705   0.000323   0.014028 (  0.014139)
Cary with arr         0.016989   0.000538   0.017527 (  0.017925)

Upvotes: 2

Tatiana Karpesh
Tatiana Karpesh

Reputation: 24

    def alphabet_position(text)
      text.downcase.split('').map do |letter|
        index = ('a'..'z').find_index(letter)
        index + 1 if index
      end.compact.join(' ')
    end

Upvotes: 0

Juanse Gimenez
Juanse Gimenez

Reputation: 513

Following the answer of @Siim Liiser, also you can use scan method to get letters.

text = "The sunset sets at twelve o' clock."
irb(main):002:0> text.scan(/[a-z]/)
=> ["h", "e", "s", "u", "n", "s", "e", "t", "s", "e", "t", "s", "a", "t", "t", "w", "e", "l", "v", "e", "o", "c", "l", "o", "c", "k"]

As you can see, the letter T is uppercase and are missing, to solve this youy can use downcase before to call the method scan:

irb(main):004:0> text.downcase.scan(/[a-z]/)
=> ["t", "h", "e", "s", "u", "n", "s", "e", "t", "s", "e", "t", "s", "a", "t", "t", "w", "e", "l", "v", "e", "o", "c", "l", "o", "c", "k"]

Upvotes: 0

Siim Liiser
Siim Liiser

Reputation: 4348

In order to debug a piece of code, run it line by line. Pretty soon you'll find that:

text = "The sunset sets at twelve o' clock."
text.split(/./) #=> []

It does not do what you think it does.

I think you meant one of those:

text.split('') #=> ["T", "h", "e", " ", ...
text.chars #=> ["T", "h", "e", " ", ...

There are several more bugs but I'll leave those for you to figure out.

Upvotes: 1

Related Questions