How do I map unique values from a multi-level array to a hash of value=>array?
I have an array that looks something like this:
[[100, "one"],
[101, "one"],
[102, "one"],
[103, "two"],
[104, "three"],
[105, "three"]]
What I would like to do is create an array of hashes that looks like this
[{"one" => [100,101,102]},
{"two" => [103]},
{"three" => [104,105]}]
The num开发者_运维知识库ber portion will always be unique, the string portion will have duplicates. Every way I think about doing this I get some long function, I would like to know the "rails way" of going about this, I'm sure there's some obscure function I am missing.
Not a Rails helper, but a common Ruby idiom can get you there. A little
arr.inject({}) { |h,(v,k)| h[k] ||= []; h[k] << v; h }
will do the trick.
array = [[100, "one"], [101, "one"], [102, "one"], [103, "two"], [104, "three"], [105, "three"]]
h = Hash.new { |h,k| h[k] = []}
array.each { |a| h[a[1]] << a[0] }
If what you need is only group stuff then you can use the Rails group_by
function :
[[100, "one"],
[101, "one"],
[102, "one"],
[103, "two"],
[104, "three"],
[105, "three"]].group_by { |a| a[1] }
=> #<OrderedHash {"three"=>[[104, "three"], [105, "three"]],
"two"=>[[103, "two"]],
"one"=>[[100, "one"], [101, "one"], [102, "one"]]}
Not to far from what you need. So if you can use it as it stand, I guess that's fine, but if you need exaclty the format you said. I think it easier to do it yourself rather than usign this and converting.
There are no really function to do that. You need use inject and create yourself you hash.
As shingara points out, this is pretty specific to the format of the array(s). You can do what you need like so:
a = [...your data...]
r = a.inject( {} ) do |h, el|
h[el.last] ||= []
h[el.last] << el.first
h
end
That gives a result like: {'one' => [101, 102], ... }
which is better than your request for an array of one-key hashes, IMO.
Here's one implementation
your_array.inject(Hash.new{|h,k| h[k] = []}) do |result, (a, b)|
result[b] << a
result
end
Is there a specific reason why you need that specific format, i.e. an array of single-element hashes instead of just a bog-standard hash? Because in that case, it would literally be just
arr.group_by(&:last)
The shortest way to get exactly what you ask for that I can come up with is:
a = [[100, "one"],
[101, "one"],
[102, "one"],
[103, "two"],
[104, "three"],
[105, "three"]]
b = a.group_by(&:pop)
#=> {"three"=>[[104], [105]], "two"=>[[103]], "one"=>[[100], [101], [102]]}
which is probably what you want.
please note that a
gets ruined by this
a
#=> [[100], [101], [102], [103], [104], [105]]
if that bothers you, you can write
b = a.map(&:dup).group_by &:pop
instead.
And if you really want that format you wrote then you can add another map:
b.map{|h,k| [h => k]}
#=> [{"one" => [100,101,102]}, {"two" => [103]}, {"three" => [104,105]}]
So to sum up:
[[100, "one"],
[101, "one"],
[102, "one"],
[103, "two"],
[104, "three"],
[105, "three"]].group_by(&:pop).map{ |h,k| [h => k] }
精彩评论