views:

210

answers:

4

What's the fastest/one-liner way to convert an array like this:

[1, 1, 1, 1, 2, 2, 3, 5, 5, 5, 8, 13, 21, 21, 21]

...into an array of objects like this:

[{1 => 4}, {2 => 2}, {3 => 1}, {5 => 3}, {8 => 1}, {13 => 1}, {21 => 3}]

+4  A: 

super basic, finally understand inject:

array.inject({}) { |h,v| h[v] ||= 0; h[v] += 1; h }

Not totally there, but almost

viatropos
why do you ask if you know the answer?
Peter Kofler
It'scertainly not illegal to answer your own question. The SO FAQ even says it's okay, as long as it's in a question format.
Twisol
to help other people find good information. it doesn't matter who answers it. don't worry, I don't have the desire to go around writing questions to things I have answers lined up for :).
viatropos
There is a badge for that even called self-learner: Answered your own question with at least 3 up votes, so nothing illegal here.
khelll
+3  A: 

To achieve your desired format, you could append a call to map to your solution:

array.inject({}) { |h,v| h[v] ||= 0; h[v] += 1; h }.map {|k, v| {k => v}}

Although it still is a one-liner, it starts to get messy.

jgre
nice, that's it! thanks.
viatropos
You can clean that up a bit by using a Hash with a default value: `array.inject(Hash.new(0)) { |h,v| h[v] += 1; h }.map {|k, v| {k => v}}`
rampion
And to get it sorted (like in the question example), add `.sort_by { |o| o.keys[0] }` to it. *Now* it's messy. :)
andre-r
+2  A: 
array.inject(Hash.new(0)) {|h,v| h[v] += 1; h }

Not a huge difference but I prefer to be explicit with what I am doing. Saying Hash.new(0) replaces {} and h[v] ||= 0

Ryan Neufeld
there it is, perfect. thanks ryan.
viatropos
+2  A: 

Requires 1.8.7

a = [1, 1, 1, 1, 2, 2, 3, 5, 5, 5, 8, 13, 21, 21, 21]
h = {}
a.group_by {|e| e}.each {|k,v| h[k] = v.length}
p h  # => {5=>3, 1=>4, 2=>2, 13=>1, 8=>1, 3=>1, 21=>3}
glenn jackman