1

I have a Ruby string, for instance: "blue green yellow dog cat mouse alpha beta".

I want to replace:

  • color names with the word "color",
  • animal names with the word "animal", and
  • greek letter names with the word "letter".

In other words, in my example above, I would like the new string to be:

"color animal letter"

and not

"color color color animal animal animal letter letter"

I came up with the following method:

def convert_string(string)
    if ["cat", "dog", "mouse"].include? key.to_s
      return "animal"
    end
    if ["blue", "yellow", "green"].include? key.to_s
      return "color"
    end
    if ["alpha", "beta"].include? key.to_s
      return "letter"
    end
    return key
end

How can I improve my method to achieve what I need?

4
  • 2
    Take a look Commented Nov 16, 2015 at 4:50
  • Thanks, it works perfectly. Feel free to suggest this solution as an answer and I will gladly accept it. Commented Nov 16, 2015 at 5:25
  • If str = "alpha blue beta", what is the desired result? (That is, do you want to replace a run of animals, for example, with a single "animal"?) Can the string contain words that are not animals, colors or letters (e.g., "donuts")? Commented Nov 16, 2015 at 8:58
  • 2
    Your issue is not clear. Why does each replaced word appear only once? All instances of a kind are replaced with a single word? If so, in which position? I.e., should "blue cat green" become "color animal" or "animal color", or does it not matter? Or, do you replace only consecutive names of a kind into a word, giving you "color animal color" for "blue cat green"? Commented Nov 16, 2015 at 10:44

2 Answers 2

2

You can use gsub:

str = "blue green yellow dog cat mouse alpha beta"

str.gsub(/(cat|dog|mouse)/, 'animal')
   .gsub(/(blue|yellow|green)/, 'color')
   .gsub(/(alpha|beta)/, 'letter')
   .split.uniq.join ' '
Sign up to request clarification or add additional context in comments.

2 Comments

If str = "alpha blue beta", this returns "letter color". I assume that "letter color letter" is wanted, but clarification is required.
Thanks for your comment @CarySwoveland. In your example, we do want "letter color" and not "letter color letter", which is why I accepted the anwser of @potashin.
2

Suppose:

str = "gamma blue green yellow dog cat mouse alpha beta"

Notice that str is slightly different than the example given in the question.

I've assumed that you want to replace each run of colors (or animals or letters) in the string by the word "color" (or "animals" or "letters").

Here are two ways to do that.

#1

This uses Enumerable#chunk and Object#itself. The latter was introduced in v.2.2. For earlier versions, write ...chunk { |s| s }....

str.split.map do |word|
  case word
  when "blue", "green", "yellow"
    "color"
  when "dog", "cat", "mouse"
    "animal"
  when "alpha", "beta", "gamma"
    "letter"
  end
end.chunk(&:itself).map(&:first).join(' ')
  #=> "letter color animal letter"

map returns:

#=> ["letter", "color", "color", "color", "animal",
#    "animal", "animal", "letter", "letter"] 

which is then chunked. Denoting this array as arr, an alternative to chunking is:

arr.each_with_object([]) { |w,a| a << w if a.empty? || w != a.last }

#2

COLOR  = "color"
ANIMAL = "animal"
LETTER = "letter"

h = { COLOR  => %w{ blue green yellow },      
      ANIMAL => %w{ dog cat mouse },
      LETTER => %w{ alpha beta gamma } }.
      each_with_object({}) { |(k,v), h| v.each { |item| h[item] = k } }
 #=> {"blue"=>"color", "green"=>"color", "yellow"=>"color",
 #    "dog"=>"animal", "cat"=>"animal", "mouse"=>"animal",
 #    "alpha"=>"letter", "beta"=>"letter", "gamma"=>"letter"}

r = /
    \b        # match a word break
    (\w+)     # match a word in capture group 1
    (?:\s\1)+ # match one or more copies of the matched word, each preceded by a space
    \b        # match a word break
    /x        # extended or free-spacing mode

str.gsub(/\w+/,h).gsub(r,'\1')
  #=> "letter color animal letter"

or

str.split.map { |word| h[word] }.chunk(&:itself).map(&:first).join(' ')
  #=> "letter color animal letter"

1 Comment

Thanks for your answer. Please see my comment on @potashin's answer for clarification.

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.