# Clean up this Ruby code under DRY principle

I've got the following code, which works, but seems repetitive. Under DRY principle, how can I clean this up?

puts "Blurt out a word"
word = gets.chomp
word_list = []
word_list.push word

until word == ""
word = gets.chomp
word_list.push word
end

puts word_list.sort


## migrated from stackoverflow.comAug 14 '12 at 18:23

This question came from our site for professional and enthusiast programmers.

puts "Blurt out a word"
word_list = []
while word_list.last != ""
word_list << gets.chomp
end
puts word_list.sort


If you prefer the until:

puts "Blurt out a word"
word_list = []
until word_list.last == ""
word_list << gets.chomp
end
puts word_list.sort


You can also do it shorter:

puts "Blurt out a word"
word_list = []
word_list << gets.chomp until word_list.last == ""
puts word_list.sort


Remark: The last (empty) line is also returned. To skip the empty line you may use:

puts word_list[0..-2].sort


Perhaps

puts 'Blurt..'
puts STDIN.take_while{|line| line !~ /^\$/}.map(&:chomp).sort


Edit: Why is this better? Because unlike other approaches it never modifies a variable that is assigned once. Thus it preserves the referential integrity. That is this is a functional programming approach in comparison with others which (as of now) follow an imperative model. The succinctness that results from it is just a bonus.

• you code solo, don't you? – Ryan Miller Aug 15 '12 at 19:24
• too hard to read? – rahul Aug 16 '12 at 2:51
• i can read it just fine, understanding what it is doing is taking a lot longer than it should. – Ryan Miller Aug 16 '12 at 13:55
• +1. @Ryan: This is simple functional programming style, IMO we should expect a competent team to have no problems with it. @rahul: If you have the abstraction String#present? you can even write a slightly more declarative STDIN.take_while(&:present?).map(&:chomp).sort. – tokland Aug 25 '12 at 19:26

Keeping as much of your style as possible, the simple way to avoid repetition is to pull the repetitive phrases inside your loop.

Now I'm assuming that what prevented you from doing this was the fact that Ruby's 'until' loop evaluates the exit condition at the top of the loop, which will therefore throw an error unless you've initialised 'word'.

This inconvenience can be avoided by moving from an 'until' loop to an infinite loop with an internal test / decision / exit condition, like so:

puts "Blurt out a word"
word_list = []
loop do
word= gets.chomp
if word == ""
break
else
word_list.push word
end
end
puts word_list.sort


Now 'word' is defined before the exit condition is evaluated. And as a useful aside, a blank entry is not inserted into 'word_list'.

There are of course many other ways of doing this -- Ruby allows for cleverness over clarity. The above simply refactors your code and removes the repetition.