From: Daniel Moore on
[Note: parts of this message were removed to make it a legal post.]

Many members of the Ruby Community contributed solutions to this quiz. Some
long time veterans as well as first time contributors. Thanks everyone for
the great turnout!

`Set#divide` is an interesting method that came up during the discussion. I
was not previously familiar with it, time to learn.

> From Ruby-Doc[2]:
> Divides the set into a set of subsets according to the commonality defined
> by the given block.
>
> If the arity of the block is 2, elements o1 and o2 are in common if
> block.call(o1, o2) is true. Otherwise, elements o1 and o2 are in common
> if block.call(o1) == block.call(o2).
> e.g:
>
> require 'set'
> numbers = Set[1, 3, 4, 6, 9, 10, 11]
> set = numbers.divide { |i,j| (i - j).abs == 1 }
> p set # => #<Set: {#<Set: {1}>,
> # #<Set: {11, 9, 10}>,
> # #<Set: {3, 4}>,
> # #<Set: {6}>}>

I didn't quite get it at first so I went to the console and tried some other
examples.

set = numbers.divide { |i,j| (i - j).abs == 2 }
=> #<Set: {#<Set: {10}>, #<Set: {1, 3}>, #<Set: {6, 4}>, #<Set: {11,
9}>}>

Ok, so the first example gets contiguous runs (numbers that are 1 apart),
and the second example gets contiguous skip runs (runs of numbers that are 2
apart).

Now to test out the single argument version:

set = numbers.divide { |i| i%2 }
=> #<Set: {#<Set: {11, 1, 3, 9}>, #<Set: {6, 4, 10}>}>

Dividing a set into odds and evens. A core component of this quiz is
grouping sets; this may come in handy.

brabuhr's first solution uses this method and is a good illustration of the
principle behind the problem.

require 'set'

class Set
def intersect?(other)
other.each { |o| return true if include?(o) }
false
end
end

def distinct_sets(array_of_arrays)
set_of_sets = array_of_arrays.map{|a|
a.to_set
}.to_set

set_of_sets.divide{|i, j|
i.intersect?(j)
}.map{|s|
s.flatten.to_a
}
end

In this solution an instance method `intersect?` is added to `Set`. This
allows us to `divide` all the sets that share an element into groups. Then
all that is left is to merge the groups of sets (`Set#flatten` takes care of
that) and to present the result as an array of arrays to match how the
output was specified in the quiz.

During the quiz discussion a full set of test cases was developed. This
enabled everyone to check and verify the accuracy of their solutions. The
test suite was provided by Rob Biedenharn and uses Shoulda[1], a testing
framework that provides additional helpers, macros, and assertions to the
Test::Unit framework.

Another benefit the testing provided was the ability to focus on the speed
at which the solutions run. When you have a full test suite you can modify
code without fear of breaking things in order to optimize and squeeze out
that last bit of speed, or conversely, to clean things up to improve code
readability, knowing that you have a safety net of tests to catch any errors
introduced.

There were many, many more solutions to this week's quiz. The principle of
grouping and merging the sets is followed by all solutions, with varying
tradeoffs between execution speed and readability. brabuhr had two more,
Benoit Daloze had two, lith had two, Rob Biedenharn had one, and first time
correspondent Johnathon Wright had one. Please be sure to take a look inside
the archived files, there are lots of good solutions in there.

Special thanks to everyone who participated in the quiz!

Distinct Sets (#225) - Solutions[3]

[1]: http://github.com/thoughtbot/shoulda
[2]: http://ruby-doc.org/core/classes/Set.html#M001626
[3]: http://rubyquiz.strd6.com/quizzes/225.tar.gz