开发者

Sort and store values from multidimensional array in new array in Ruby

开发者 https://www.devze.com 2022-12-09 20:02 出处:网络
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\

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消