364d0e86994a268906392f6b6146af38

Here is the 5th edition of Rubyize this. You can find the original blog post on ruby fleebie here : http://www.rubyfleebie.com/rubyize-this-5th-edition/

Tom wrote a very simple ruby script to highlight some words in a text. He chose to highlight the words with asterisks, like *that* (In 2 months, perhaps Tom will have to use his script to produce HTML output or something else... he will be screwed with those basic asterisks). Tom has absolutely no ruby background, help him refactor his code <em>ala</em> Ruby, that is : short, clean and simple.

By the way, Tom didn't put a lot of effort in his algorithm. His dumb gsub thing will mistakenly replace <em>parts of words</em> instead of whole words only. Maybe some improvements would be needed there.

1
2
3
4
5
6
7
@wordsToHighlight=["important","monkey","dancing"]
def highlightText(input)
  for i in 0..@wordsToHighlight.length-1 do
    input = input.gsub(@wordsToHighlight[i],"*" + @wordsToHighlight[i] + "*")
  end
  return input
end

Refactorings

No refactoring yet !

Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

January 3, 2008, January 03, 2008 14:26, permalink

No rating. Login to rate!

Here's my take, it's short but not perfect

1
2
3
4
5
6
def highlight_text(input, words_to_highlight=%w(important monkey dancing))
  input.gsub(/#{words_to_highlight.join('|')}/i, '*\0*')
end

puts highlight_text("Hey monkey, it's important you start dancing right now!")
# >> Hey *monkey*, it's *important* you start *dancing* right now!
F9ac05fdd57915983ccea58699e1f942

mvanholstyn

January 3, 2008, January 03, 2008 14:33, permalink

No rating. Login to rate!
1
2
3
def highlight_text(input, words_to_highlight = [], wrapper = '*\1*')
  input.gsub(/\b(#{words_to_highlight.join("|")})\b/, wrapper)
end
25ecb172aeb33367b20b862ecfe626f6

Jason

January 3, 2008, January 03, 2008 14:34, permalink

No rating. Login to rate!

this dumbass ajax form is not working!

1
2
3
4
5
6
def highlightText(input, open_tag, close_tag)
  input.split(/ /).collect {|word| @wordsToHighlight.include?(word) ? "#{open_tag}#{word}#{close_tag}" : word }.join(" ")
end

input = "The world of very important things contained many monkeys that really liked dancing around!"
p highlightText(input, "<b>", "</b>")
25ecb172aeb33367b20b862ecfe626f6

Jason

January 3, 2008, January 03, 2008 14:38, permalink

No rating. Login to rate!

Revision 2, this time passing in list of words to highlight as a function argument like macournoyer did (first reply), which is much better than accessing a named variable outside of the function.

1
2
3
4
5
6
7
def highlightText(input, open_tag, close_tag, words_to_highlight)
  input.split(/ /).collect {|word| words_to_highlight.include?(word) ? "#{open_tag}#{word}#{close_tag}" : word }.join(" ")
end

list_of_words=["important","monkey","dancing"]
input = "The world of very important things contained many monkeys that really liked dancing around!"
p highlightText(input, "<b>", "</b>", list_of_words)
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

January 3, 2008, January 03, 2008 14:41, permalink

No rating. Login to rate!

Sorry about the form not working Jason, should be fixed now

1a18d240b68c5cad64a205a8c17442a7

Justin Jones

January 3, 2008, January 03, 2008 15:15, permalink

No rating. Login to rate!

Good solutions above. This is overkill, but can't think of anything else that hasn't already been done.

1
2
3
4
5
6
7
8
9
class String
  def hilight(marker = '*', *args)
    gsub(/\b(#{args.join('|')})\b/, "#{marker}\\1#{marker}")
  end
end

words = %w(monkeys dancing important)
input = "The world of very important things contained many monkeys that really liked dancing around!"
input.hilight('*', *words)
D16d53391068ff0830269149b060789d

Jason Dew

January 3, 2008, January 03, 2008 17:24, permalink

No rating. Login to rate!

i purposely wrote this without looking at the refactorings... funny how close it still is but it's a bit different. a little more verbose imo but extensible. the only issue i have with it is that you are limited to one a single delimiter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Highlighter

  def initialize(keywords, prefix='*', suffix='*', delimiter=' ')
    @keywords, @prefix, @suffix, @delimiter = keywords, prefix, suffix, delimiter
  end

  def highlight(text)
    text.split(@delimiter).collect do |word|
      @keywords.include?(word) ? highlight_word(word) : word
    end.join(@delimiter)
  end

  private

  def highlight_word(word)
    @prefix + word + @suffix
  end

end

highlighter = Highlighter.new %w(important monkey dancing)

text = "there are some very important monkeys dancing the most important of all dances in monkey land"
highlighted_text = highlighter.highlight(text)

puts "ORIGINAL   : #{text}"
puts "HIGHLIGHTED: #{highlighted_text}"
1a18d240b68c5cad64a205a8c17442a7

Justin Jones

January 3, 2008, January 03, 2008 20:05, permalink

No rating. Login to rate!

I'm sure the String#pad (not sure about the name either) utility function added here is available somewhere, but anyways. Also using the block version of gsub this time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class String
  def pad(char = ' ')
    "#{char}#{self}#{char}"
  end

  def highlight(marker = '*', *args)
    gsub(/\b(#{args.join('|')})\b/) { |word| word.pad(marker) }
  end
end

words = %w(monkeys dancing important)
input = "The world of very important things contained many monkeys that really liked dancing around!"

input.highlight('*', *words)
49355dda8386e6c1262164a9e4075ef4

Jens Ruzicka

January 6, 2008, January 06, 2008 14:27, permalink

No rating. Login to rate!

There are already better versions here

1
2
3
4
5
6
7
8
9
10
11
12
13
def highlight_text words_to_highlight, fore="*", after="*" 
	self.split.each do |token| 
		if words_to_highlight =~ token
			STDOUT << "#{fore} #{$&} #{after} "
		else
			STDOUT << "#{token} "
		end
	end
end

line = "This is an important text about a dancing monkey"
line.highlight_text %r{important|dancing|monkey}, "<b>", "</b>"
gets
5ef95bf95af5064ff3c2bcafa6a8a212

David Madden

January 7, 2008, January 07, 2008 13:51, permalink

No rating. Login to rate!

Nothing major here, just made it case insensitive. An issue I have is that hyphenated words like "monkey-wrench" will still get highlighted and I have no idea how to get the regexp to ignore words beginning or ending with a -.

1
2
3
4
5
6
def highlight_text(input, words=%w(important monkey dancing), sym='*')  
  input.gsub(/\b(#{words.join("|")})\b/i, "#{sym}\\1#{sym}")
end


puts highlight_text("This is the Important text to highlight. Can you see the dancing monkey-wrench?")
814aa386c3b6d13945b3b43912715551

Mike

January 9, 2008, January 09, 2008 04:55, permalink

No rating. Login to rate!

Probably went overboard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class String
  def highlight(mark='*')
    mark+self+mark
  end
end

class Sentence
  attr_writer :words_to_highlight
  def initialize(string='')
    @string = string
    @words_to_highlight = %w()
  end
  def highlight(words_to_highlight=@words_to_highlight,mark='*')
    all_words = @string.split(/\b/)
    highlighted = all_words.collect do |word| 
      ( words_to_highlight.include?(word) ) ? word.highlight : word
    end
    highlighted.join()
  end
  def to_s
    @string
  end
end

sentence = Sentence.new("hello world, whats happening?")
puts sentence
puts sentence.highlight(%w(world whats))
sentence.words_to_highlight = %w(world)
puts sentence.highlight
814aa386c3b6d13945b3b43912715551

Mike

January 9, 2008, January 09, 2008 05:07, permalink

No rating. Login to rate!

another one, neither of these work on contractions, this one features recursion and only involves modifying string.

This one outputs:
*hello* world, what's *up*?

1
2
3
4
5
6
7
8
9
10
11
class String
  def highlight(words=%w(), mark='*')
    if ( self.split(/\b/).length == 1 )
      words.include?(self) ? mark+self+mark : self
    else
      self.split(/\b/).collect { |w| w.highlight(words) }.join
    end
  end
end

puts "hello world, what's up?".highlight(%w(hello up))
814aa386c3b6d13945b3b43912715551

Mike

January 9, 2008, January 09, 2008 05:11, permalink

No rating. Login to rate!

found a bug in that one, didnt work when you had a full sentence and passed in a mark.

This one will correctly output +hello+ world, what's +up+?

I'm going to stop now, sorry for the triple post.

1
2
3
4
5
6
7
8
9
10
11
class String
  def highlight(words=%w(), mark='*')
    if ( self.split(/\b/).length == 1 )
      words.include?(self) ? mark+self+mark : self
    else
      self.split(/\b/).collect { |w| w.highlight(words, mark) }.join
    end
  end
end

puts "hello world, what's up?".highlight(%w(hello up), '+')
865351a8b8bb963c7ee935b3f342854e

Simon Gate

January 9, 2008, January 09, 2008 23:43, permalink

No rating. Login to rate!

How i did it.

1
2
3
4
5
6
7
class String
  def highlight(words = nil, mark = "*")
    words.nil? ? self : self.gsub(/#{words.join("|")}/) {|s| "#{mark}#{s}#{mark}"}
  end
end

puts "A very important list is in monkey dancing tonight with pants down in rain today.".highlight %w(monkey dancing today)
8802b1fa1b53e2197beea9454244f847

Sean Cribbs

January 26, 2008, January 26, 2008 18:11, permalink

No rating. Login to rate!

Regexp.union is your friend! This also demonstrates a typical use of the splat operator to turn an array into an argument list, and the usage of back-references in global substitutions. One thing that I found recently is that your replacement string needs to be in single-quotes or Ruby will turn your \0,\1, etc references into the corresponding ASCII characters instead of the backreferences! If you use a double-quoted string, \\0, \\1 etc. should accomplish the same thing.

1
2
3
4
5
6
7
8
@wordsToHighlight=["important","monkey","dancing"]

def highlightText(input)
  input.gsub(Regexp.union(*@wordsToHighlight), '*\0*')
end

puts highlightText("I think it's important to have a dancing monkey in your bedroom.")
# => I think it's *important* to have a *dancing* *monkey* in your bedroom.
8802b1fa1b53e2197beea9454244f847

Sean Cribbs

January 26, 2008, January 26, 2008 18:16, permalink

1 rating. Login to rate!

Now having read some of the others, I'll borrow from Simon's idea:

1
2
3
4
5
6
7
8
class String
  def highlight(words=[])
    words.empty? ? self : self.gsub(Regexp.union(*words), '*\0*')
  end
end

puts "I think it's important to have a dancing monkey in your bedroom.".highlight %w(important monkey dancing)
# => I think it's *important* to have a *dancing* *monkey* in your bedroom.

Your refactoring





Format Copy from initial code

or Cancel