4
\$\begingroup\$

I watched a presentation by Dave Thomas on Elixir where he gave an example problem that he solved using functional programming. He mentioned using Ruby to solve the same problem, but did not show an example. I decided to give it a try. The problem goes something like this:

For a list (Ruby doesn't have lists, but for our purposes an Array is close enough) of n numbers, create a new list where unique numbers are represented once, and repeated numbers are represented by a tuple (again, Ruby doesn't have tuples, but for our purposes a Hash or an Array would do) where the first element is the number itself, and the second is its count.

For Example, the following list:

[ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]

Would become:

[ 1, {2, 3}, 3, {4, 2}, 5, {6, 3} ]

I came up with the following solution.

list = [ 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6 ]
list.each_with_object(Hash.new(0)) { |n, hash| hash[n] += 1 }.map { |k, v| v > 1 ? { k => v } : k }
#=> [ 1, {2 => 3}, 3, {4 => 2}, 5, {6 => 3} ]

How would you go about solving this problem?

\$\endgroup\$
4
\$\begingroup\$

Some notes:

  • From the moment you write hash[n] += 1, your solution is not functional anymore.
  • each_with_object is also imperative, for FP you should use reduce.
  • reduce works well with linked lists, not so well for generating new arrays. In any case, reduce is a generic abstraction, there is a more specific one to the problem at hand: Enumerable#chunk.

That's how I'd write it:

xs.chunk(&:itself).map { |y, ys| ys.size == 1 ? y : {y => ys.size} }
\$\endgroup\$
  • \$\begingroup\$ Thanks great. I am wondering about &:itself, there's no mention of it in the docs. Is that a shortcut or a keyword? \$\endgroup\$ – Mohamad Apr 13 '15 at 20:10
  • \$\begingroup\$ ruby-doc.org/core-2.2.1/Object.html#method-i-itself \$\endgroup\$ – tokland Apr 13 '15 at 20:11
  • \$\begingroup\$ Ahhh, ok, I was looking at Enumerable. So I suppose &:itself translates to chunk { |n| n.itself }, also the same as chunk { |n| n } \$\endgroup\$ – Mohamad Apr 13 '15 at 20:12
  • \$\begingroup\$ @Mohamad that is correct, and by extension &:any_method is translated the same way \$\endgroup\$ – Devon Parsons Apr 14 '15 at 17:24
  • 1
    \$\begingroup\$ Immutability is a key concept of functional programming. You can't change anything in-place (add, update, remove, and so on). goo.gl/4OyjP \$\endgroup\$ – tokland Apr 15 '15 at 18:46
1
\$\begingroup\$

I would write a slower but easier to read version:

def same_or_tuple(list)
    list.map {|x| if list.count(x) == 1 then x
                  else [x, list.count(x)] end}
        .uniq
end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.