Categories
Uncategorized

Enumerating Enumerable: Enumerable#each_slice

Enumerating Enumerable

Ten installments already? That’s right, this is the tenth Enumerating Enumerable article. As I’m fond of repeating, this is my little contribution to the Ruby community: a series of articles where I attempt to do a better job at documenting Ruby’s Enumerable module than Ruby-Doc.org does, with pretty pictures and more in-depth examples!

In this article, I’m going to cover each_slice, which got introduced in Ruby 1.9.

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
  8. drop_while
  9. each_cons

Enumerable#each_slice Quick Summary

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

In the simplest possible terms Given a number n, split the array into n-element slices (if the number of elements in the array isn’t evenly divisible by n, just put the remaining elements in the last slice), then iterate through those slices.
Ruby version 1.9 only
Expects
  • A number n describing the size of the iteration slice.
  • An optional block to receive the values from each iteration.
Returns
  • nil, if used with a block.
  • An Enumerator object that outputs n-sized slices of the collection, if used without a block.
RubyDoc.org’s entry Enumerable#each_slice

Enumerable#each_slice and Arrays

When used on an array with a block, each_slice takes a numeric argument n and splits the array into slices of length n (if the number of elements in the array isn’t evenly divisible by n, the remaining elements are put into the last slice). each_slice then iterates through the set of slices, passing each slice to the block. After the final iteration, each_slice returns nil.

Since this is yet another case when an showing an example makes things very clear, I’ll do just that, using the same example data I used in the article on each_cons:

justice_league = ["Aquaman", "Batman", "Black Canary", 
                  "Flash", "Green Arrow", "Green Lantern", 
                  "Martian Manhunter", "Superman", 
                  "Vixen", "Wonder Woman"]
justice_league = ["Aquaman", "Batman", "Black Canary", "Flash", "Green Arrow",
"Green Lantern", "Martian Manhunter", "Superman", "Vixen", "Wonder Woman"]

justice_league.each_slice(3) {|team| p team}
=> ["Aquaman", "Batman", "Black Canary"]
["Flash", "Green Arrow", "Green Lantern"]
["Martian Manhunter", "Superman", "Vixen"]
["Wonder Woman"]
=> nil

When used on an array without a block, each_slice takes a numeric argument n and returns an Enumerator that outputs slices of the array when its next method is called. Here’s an example:

teams_of_3 = justice_league.each_slice(3)
=> #<Enumerable::Enumerator:0x007fc73baa9830>

teams_of_3.next
=> ["Aquaman", "Batman", "Black Canary"]

teams_of_3.next
=> ["Flash", "Green Arrow", "Green Lantern"]

teams_of_3.next
=> ["Martian Manhunter", "Superman", "Vixen"]

teams_of_3.next
=> ["Wonder Woman"]

teams_of_3.next
=> StopIteration: iteration reached at end...
# (I'm skipping the rest of the error message)

teams_of_3.rewind
=> #<Enumerable::Enumerator:0x007fc73baa9830>

teams_of_3.next
=> ["Aquaman", "Batman", "Black Canary"]

Enumerable#each_slice and Hashes

When used on an hash with a block, each_slice takes a numeric argument n and splits the hash into arrays of length n (if the number of elements in the array isn’t evenly divisible by n, the remaining elements are put into the last slice). Within these arrays, each hash element is represented as a two-element array, with the first element being the key and the second element being the corresponding value.

each_slice then iterates through the set of arrays, passing each array to the block. After the final iteration, each_slice returns nil.

Here’s an example, using the same example data I used in the article on each_cons:

enterprise_crew = {:captain => "Picard",
                   :first_officer => "Riker",
                   :science_officer => "Data",
                   :tactical_officer => "Worf",
                   :chief_engineer => "LaForge",
                   :chief_medical_officer => "Crusher",
                   :ships_counselor => "Troi",
                   :annoying_ensign => "Crusher",
                   :attractive_ensign => "Ro",
                   :expendable_crew_member => "Smith"}
=> {:captain=>"Picard", :first_officer=>"Riker", :science_officer=>"Data",
:tactical_officer=>"Worf", :chief_engineer=>"LaForge",
:chief_medical_officer=>"Crusher", :ships_counselor=>"Troi",
:annoying_ensign=>"Crusher", :attractive_ensign=>"Ro",
:expendable_crew_member=>"Smith"}
=> nil

enterprise_crew.each_slice(3) {|team| p team}
=> [[:captain, "Picard"], [:first_officer, "Riker"], [:science_officer, "Data"]]
[[:tactical_officer, "Worf"], [:chief_engineer, "LaForge"],[:chief_medical_officer, "Crusher"]]
[[:ships_counselor, "Troi"], [:annoying_ensign, "Crusher"], [:attractive_ensign,  "Ro"]]
[[:expendable_crew_member, "Smith"]]
=> nil

When used on a hash without a block, each_slice takes a numeric argument n and returns an Enumerator that outputs arrays when its next method is called. Here’s an example:

away_teams_of_3 = enterprise_crew.each_slice(3)
=> #<Enumerable::Enumerator:0x007fc73ba44c50>

away_teams_of_3.next
=> [[:captain, "Picard"], [:first_officer, "Riker"], [:science_officer, "Data"]]

away_teams_of_3.next
=> [[:tactical_officer, "Worf"], [:chief_engineer, "LaForge"], [:chief_medical_officer, "Crusher"]]

away_teams_of_3.next
=> [[:ships_counselor, "Troi"], [:annoying_ensign, "Crusher"], [:attractive_ensign, "Ro"]]

away_teams_of_3.next
=> [[:expendable_crew_member, "Smith"]]

away_teams_of_3.next
=> StopIteration: iteration reached at end...
# (I'm skipping the rest of the error message)

away_teams_of_3.rewind
=> #

away_teams_of_3.next
=> [[:captain, "Picard"], [:first_officer, "Riker"], [:science_officer, "Data"]]