codeinspired
codeinspired

Reputation: 366

Writing a regexp as argument to gsub! method -- stumped?

In my Rails 4.2.6 / Ruby 2.2.4 app, I have a db query method that generates a string; for example:

"Chest - pectoralis, Quads - quadriceps, Hamstrings - biceps femoris"

However, the result I want is:

"Chest, Quads, Hamstrings"

The domain tracks 21 muscle groups, which means the generated string can have a lot of different combinations. However, the string will always have the same pattern, that is: "common muscle name - technical muscle name".

With conditional logic, I can get the result I want by passing "string" arguments to the gsub method, like this:

 if @muscle_group_ids == [1, 13, 14]
    @muscle_group_names = MuscleGroup.where(id: @muscle_group_ids).map { |n|   n[:name]
   }.join(', ').gsub!("Chest - pectoralis", "Chest").gsub!("Quads -    quadriceps", "Quads").gsub!("Hamstrings - biceps femoris", "Hamstrings")
end
=> "Chest, Quads, Hamstrings"

That approach isn't viable; it's tedious and brittle. There must be a better way to remove dashes and technical names from the generated strings. I'm stumped on how to best accomplish this. Is it possible to write a regexp to achieve the desired result, and how? Is there an easier, better, cleaner approach to achieve what I'm trying to do? Thanks!

Upvotes: 2

Views: 175

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110675

 r = /[[:alpha:]]+     # match one or more letters
      [\s+[:alpha:]+]* # match one or more spaces followed by one or more letters,
                       # zero or more times, to match any words after the first
      (?=\s+-\s+)      # match a hyphen surrounded by whitespace in a positive lookahead
     /x                # free-spacing regex definition mode

"Chest - pectoralis, Quads - quadriceps, Achilles heel - Achilles tendon".scan(r)
  #=> ["Chest", "Quads", "Achilles heel"]

Upvotes: 2

Mark Reed
Mark Reed

Reputation: 95252

Like this?

irb(main):001:0> s="Chest - pectoralis, Quads - quadriceps, Hamstrings - biceps femoris"
irb(main):002:0> s.gsub(/(\w+)\s+-\s+[^,]*/, '\1')
=> "Chest, Quads, Hamstrings"

That searches for any number (+) of "word" characters (\w), which it remembers because they're in parentheses, followed by space (\s+), a hyphen, more space, and anything that's not a comma ([^,]*). It replaces all of that with just the word characters that it remembered (\1).

The pattern is pretty specific and would need to be modified if, for example, the common name might be more than one word.

Also, I just did gsub above to return (and display, in irb) the result; to modify the string in place you would use gsub!.

Upvotes: 2

Eric Duminil
Eric Duminil

Reputation: 54223

MuscleGroup#common_name

The easiest way would be to define a new method inside MuscleGroup model :

def common_name
  name.split(' - ').first
end

Your code becomes :

@muscle_group_names = MuscleGroup.where(id: @muscle_group_ids).map { |muscle_group|
  muscle_group.common_name }.join(', ')

Double split

If for some reason, you already have a complete string, a double split might be more robust and easier to read than a big regexp.

You can split around ', ' to get an array of "common muscle name - technical muscle name". You can then split each one of those strings around ' - ' and keep the first part :

"Chest - pectoralis, Quads - quadriceps, Hamstrings - biceps femoris".
  split(', ').map{|s| s.split(' - ').first}
# ["Chest", "Quads", "Hamstrings"]

Once you have the array, you can join back to a string :

"Chest - pectoralis, Quads - quadriceps, Hamstrings - biceps femoris".
  split(', ').map{|s| s.split(' - ').first}.join(', ')
#  "Chest, Quads, Hamstrings"

Upvotes: 2

Lasse Sviland
Lasse Sviland

Reputation: 1517

You could just split the name on " -" in your map function, and only return the first part like this:

if @muscle_group_ids == [1, 13, 14]
    @muscle_group_names = MuscleGroup.where(id: @muscle_group_ids).map { |n|   
        n = n[:name].split(" -")[0] 
    }.join(', ')
end
=> "Chest, Quads, Hamstrings"

Upvotes: 5

Related Questions