In Progress
Unit 1, Lesson 21
In Progress

Partition

Video transcript & code

Today we find ourselves in charge of writing software for managing the work on a farm. Anyone on the farm can submit work requests. Since this is a collective farm, all requests have equal priority. Except that some animals are more equal than others. So we have to do a little bit of sorting before parceling out the requests to be completed by each according to their ability.

Request = Struct.new(:from, :task)

REQUESTS = [
  Request.new("Snowball", "Build a bigger slop trough"),
  Request.new("Boxer", "Clean the hay cart"),
  Request.new("Napoleon", "Build a whisky still"),
  Request.new("Benjamin", "Re-stock the library"),
]

To sort out the most urgent requests from the rest, we could use #select to check the submitter against a list of names.

require "./requests"

REQUESTS.select{|r| ["Snowball", "Napoleon", "Squealer"].include?(r.from)}
# => [#<struct Request from="Snowball", task="Build a bigger slop trough">, #<struct Request from="Napoleon", task="Build a whisky still">]

But then in order to get the rest of the requests, we have to immediately follow it up with an inverted search using #reject.

require "./requests"

REQUESTS.reject{|r| ["Snowball", "Napoleon", "Squealer"].include?(r.from)}
# => [#<struct Request from="Boxer", task="Clean the hay cart">, #<struct Request from="Benjamin", task="Re-stock the library">]

This seems a bit redundant, as well as inefficient. Which brings us to #partition, one of those many Enumerable methods which we tend to forget about until suddenly we find we have a job for which it is perfectly suited.

We replace our #select with #partition, and assign the result to a pair of variables - one for the "priority" tasks, and one for the rest. The full result of this message is an array of two arrays. Inspecting the variables we destructured the two arrays into, we can see that we have neatly divided our list of tasks into high- and low-priority lists, in a single pass.

require "./requests"

priority, rest = REQUESTS.partition{ |r|
  ["Snowball", "Napoleon", "Squealer"].include?(r.from)
}
priority
# => [#<struct Request from="Snowball", task="Build a bigger slop trough">, #<struct Request from="Napoleon", task="Build a whisky still">]
rest
# => [#<struct Request from="Boxer", task="Clean the hay cart">, #<struct Request from="Benjamin", task="Re-stock the library">]

And that's the #partition method in a nutshell. Next time you come across a situation calling for splitting a collection into two subsets based on some criterion, you can think back to this episode and solve it neatly. Happy hacking!

Responses