Stepdeff
Stepdeff

Reputation: 21

Ruby - Strings & Symbols Questions

Context: just started learning Ruby, the code below is a part of a slightly longer program, but I only need some help with the first part, and I may not articulate my problem clearly enough, so apologies in advance

Question: I've marked the problem parts of the program. If I have a hash with strings and I want the if-else statement to identify user input, regardless of how a user enters the movie title (e.g. different spacing, capitalization, or without quotation marks) as the same as those in the hash and output the else statement, how would I go about doing that? Thank you

movies = {
  "Lord of the Rings" => 4,     # <= this
  Inception: 4, 
  "Hurt Locker" => 3.5,         # <= this
  Mother: 4
}
puts "What would you like to do?"
puts "-- Type 'add' to add a movie."
puts "-- Type 'update' to update a movie."
puts "-- Type 'display' to display a movie."
puts "-- Type 'delete' to delete a movie."

choice = gets.chomp

case choice
when "add"
  puts "What movie would you like to add? "
  title = gets.chomp
  if movies[title.to_sym].nil? 
    puts "What rating does the movie have? "
    rating = gets.chomp
    movies[title.to_sym] = rating.to_i
  else
    #                                         and this
    #                                                v
    puts "That movie already exists! Its rating is #{movies[title.to_sym]}."
  end
end

Upvotes: 1

Views: 128

Answers (1)

Alex
Alex

Reputation: 30131

You have a hash with strings and symbols:

{
  "Lord of the Rings" => 4,     # <= string
  Inception:             4      # <= symbol
}

# NOTE: it is the same as this
{
  "Lord of the Rings" => 4,     # <= string
  :Inception          => 4      # <= symbol
}

Because user input is a string, changing the original hash keys to be only strings from the start, seems like the right thing to do.

movies = {
  "Lord of the Rings" => 4,
  "Inception"         => 4, 
  "Hurt Locker"       => 3.5,
  "Mother"            => 4
}

Users can't be trusted to do the most basic things, like typing the thing they see on the screen. So, some input processing need to happen:

def normalize_string something
  something
    .to_s                    # make sure it's a string
    .downcase                # make everything a lowercase
    .gsub(/[^[:alnum:]]/,'') # remove anything but letters and numbers
end

This function can be used like this:

movies = {"Lord of the Rings"=>4, :Inception=>4, "Hurt Locker"=>3.5, :Mother=>4}
user_input = " lordOF the ring s   \n\n\n"

>> movies.transform_keys{|k| normalize_string(k)}
=> {"lordoftherings"=>4, "inception"=>4, "hurtlocker"=>3.5, "mother"=>4}

>> normalize_string(user_input)
=> "lordoftherings"

>> movies.transform_keys{|k| normalize_string(k)}[normalize_string(user_input)]
=> 4

Hash keys and user input are processed by the same function where we try to arrive at one common string.

normalized_movies = movies.transform_keys{|k| normalize_string(k)}

case choice
when "add"
  puts "What movie would you like to add? "
  title = gets.strip # .chomp
  normalized_title = normalize_string(title)

  # flip if/else instead of .nil?
  if (rating = normalized_movies[normalized_title]) 
    puts "That movie already exists! Its rating is #{rating}."
  else
    puts "What rating does the movie have? "
    movies[title] = gets.chomp.to_i
    #      ^
    # NOTE: use the original title when creating a new key
    #       with spaces and capitalization.
  end
end

https://rubyapi.org/3.1/o/regexp

https://rubyapi.org/3.1/o/hash#method-i-transform_keys

https://rubyapi.org/3.1/o/string#method-i-strip

Upvotes: 2

Related Questions