# Transforming an array in Ruby

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?

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} }

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

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