Sort and store values from multidimensional array in new array in Ruby
I have the following array:
votes_array = [["2", "1"], ["2", "4"], ["4", "3"], ["3", "4"], ["1", "N"], ["3", "1"], ["1", "2"], ["4", "1"], ["0", "1"], ["0", "2"], ["1", "3"], ["1", "4"]]
And I want to create a new array (or hash) that stores the votes开发者_如何转开发_array items by their first option so that the new array looks like this:
candidate_votes = [
{"id" => "0", "votes" => [["0", "1"],["0","2"]]},
{"id" => "1", "votes" => [["1", "N"],["1","2"],["1","3"],["1","4"]]},
etc,
etc]
The order of the votes within the 'votes' key is not important, just that all the votes get split into the relevant ids.
I've got it working with the following code:
first_preferences = votes_array.map(&:first)
valid_candidates = first_preferences.uniq
valid_candidates.each do |candidate|
these_votes = votes_array.find_all { |i| i[0] == candidate }
candidate_votes << {"id" => candidate, "votes" => these_votes}
end
But wondered if there's a more elegant, cleaner or Rubiyst way?
With Ruby 1.8.7+, it's a one-liner:
candidate_votes = votes_array.group_by {|pair| pair[0]}
Pretty printing candidate_votes
returns
{"0"=>[["0", "1"], ["0", "2"]],
"1"=>[["1", "N"], ["1", "2"], ["1", "3"], ["1", "4"]],
"2"=>[["2", "1"], ["2", "4"]],
"3"=>[["3", "4"], ["3", "1"]],
"4"=>[["4", "3"], ["4", "1"]]}
Here's one that creates candidate_votes
as a Hash. It's probably a little faster because you need to iterate votes_array
only once:
candidate_votes = {}
votes_array.each do |id, vote|
candidate_votes[id] ||= {"id" => id, "votes" => []}
candidate_votes[id]["votes"] << [id, vote]
end
This will give results like this:
candidate_votes = {
"0" => {"votes" => [["0", "1"], ["0", "2"]], "id" => "0"},
... etc ...
}
I think you can organize the output a lot more concisely using something like this:
# Use Enumerable#inject to convert Array to Hash
results = votes_array.inject({ }) do |hash, vote|
# Create hash entry for candidate if not defined (||=)
hash[vote[0]] ||= { :votes => [ ] }
# Add vote to candidate's array of votes
hash[vote[0]][:votes] << vote[1]
# Keep hash for next round of inject
hash
end
# => {"0"=>{:votes=>["1", "2"]}, "1"=>{:votes=>["N", "2", "3", "4"]}, "2"=>{:votes=>["1", "4"]}, "3"=>{:votes=>["4", "1"]}, "4"=>{:votes=>["3", "1"]}}
As a note it's advantageous to use Symbols for Hash keys in Ruby since they are generally more efficient than Strings for that kind of application.
精彩评论