Enumerating Enumerable: Enumerable#drop_while

by Joey deVilla on July 25, 2008

After the wackiness of the past couple of weeks — some travel to see family, followed by a busy week of tech events including DemoCamp 18, Damian Conway’s presentation, FAILCamp and RubyFringe — I’m happy to return to Enumerating Enumerable, the article series in which I attempt to do a better job at documenting Ruby’s Enumerable module than Ruby-Doc.org does.

In this article, the eighth in the series, I’m going to cover a method introduced in Ruby 1.9: drop_while.

I’m going through the Enumerable‘s methods in alphabetical order. If you missed any of the earlier articles, I’ve listed them all below:

  1. all?
  2. any?
  3. collect / map
  4. count
  5. cycle
  6. detect / find
  7. drop

Enumerable#drop_while Quick Summary

Graphic representation of the \"drop_while\" method in Ruby\'s \"Enumerable\" module

In the simplest possible terms Given a collection and a condition, return an array made of the collection’s items, starting the first item that doesn’t meet the condition.
Ruby version 1.9 only
Expects A block containing the condition.
Returns An array made up of the remaining items, if there are any.
RubyDoc.org’s entry Enumerable#drop_while

Enumerable#drop_while and Arrays

When used on an array, drop_while returns a copy of the array created by going through the original array’s items in order, dropping elements until it encounters the an element that does not meet the condition. The resulting array is basically a copy of the original array, starting at the first element that doesn’t meet the condition in the block.

As in many cases, things become clearer with some examples:

Enumerable#drop_while and Hashes

When used on a hash, drop_while effectively:

  • Creates an array based on the hash, with each element in the hash represented as a two-element array where the first element contains the key and the second element containing the corresponding value, then
  • goes through each element in the array, dropping elements until it encounters the first element that doesn’t meet the condition in the block. The resulting array is an array of two-element arrays, starting at the first element that doesn’t meet the condition in the block.

Once again, examples will make this all clear:

Enumerable#drop_while’s Evil Twin, Enumerable#take_while

I’ll cover take_while in detail in a later installment, but for now, an example should suffice:

{ 3 comments… read them below or add one }

1 Gianni Chiappetta July 25, 2008 at 11:40 pm

Aren’t these virtually the same as reject and select?

2 Joey deVilla July 26, 2008 at 10:15 am

@Gianni Chiappetta: They’re similar, but not the same.

drop_while and take_while go through the collection’s items in order and stop as soon as the condition in the block is no longer met, whereas reject and select don’t operate under that constraint.

Here’s an example showing the difference:

temperatures = [28, 25, 30, 22, 27]
=> [28, 25, 30, 22, 27]

# "reject" returns an array that is made by using the
# original collection and discarding all of its elements
# that meet the condition in the block
temperatures.reject {|temperature| temperature < 28}
=> [28, 30]

# "drop_while" is like "reject", but when working
# through the given collection in order, it stops discarding
# elements as soon as it encounters an element that
# *doesn't* meet the condition in the block
temperatures.drop_while {|temperature| temperature < 28}
=> [28, 25, 30, 22, 27]

In the drop_while example above, the first element, 28, causes the condition in the block to return false. drop_while stops checking the remaining elements and returns the result array. So in the case of the temperatures array above, drop_while‘s result is equal to the original array.

drop_while will return the same results as reject if the array is sorted in such a way that all the elements that meet the condition in the block are moved to the beginning of the collection. In the case of the temperatures array, that’s easy:

temperatures.sort.drop_while {|temperature| temperature < 28}
=> [28, 30]

The practical upshot of all this is that you can think of drop_while (and its evil twin take_while) as “lazy evaluation” versions of reject and select where the order of the collection matters.

As for practical uses for drop_while? I’m sure there are some, but I’m feeling a little lazy today and will use the standard cop-out: “I’ll leave that as an exercise for the reader.”

3 Gianni Chiappetta July 28, 2008 at 11:42 am

Ahh I see, thanks for clearing that up Joey!

Leave a Comment

{ 1 trackback }

Previous post:

Next post: