Thibaud Clement
Thibaud Clement

Reputation: 6907

Replace string if it contains one or more values included in an array

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

I want to replace:

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?

Upvotes: 1

Views: 285

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110755

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"

Upvotes: 2

potashin
potashin

Reputation: 44611

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 ' '

Upvotes: 2

Related Questions