tags:

views:

67

answers:

1

Thanks to the help in a previous question, I've got the following code returning the expected results with find_all.

Original array (sorted by votes[0]):

votes_array = [{"votes"=>[13], "id"=>"4", "elected"=>0}, {"votes"=>[12], "id"=>"1", "elected"=>0}, {"votes"=>[8], "id"=>"3", "elected"=>0}, {"votes"=>[3], "id"=>"2", "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]

And the code that runs after this array:

grouped_last = []
grouped_last = votes_array.find_all {|i| i["votes"][0] == votes_array.last["votes"][0] }
if grouped_last.length > 1
  grouped_last.each do |candidate|
    candidate["votes"][0] += 10 # another value is actually inserted here
  end
end

The problem is that it's affecting the original array - as I'm thinking it should, as find_all is just a filter.

Is there a way to perform the same function as the find all with map/collect so that it creates a new array that doesn't affect the original? My brain is fizzing trying to work this out today!

EDIT: Let's see if I can clarify this a bit.

I'm using the votes_array to store a set of values for each stage of a voting process (using Single Transferrable Vote) per candidate.

For each round the candidate with the lowest votes is eliminated, and the next preference votes for that candidate are transferred to the relevant candidates ready for the next round.

However if there is more than one candidate with the same lowest votes, then the next preference votes for each candidate are compared and the lowest number of candidates votes signals the eliminated candidate.

What I'm trying to do with this part is to find the candidate to eliminate.

I'm wanting to create a new array containing the lowest vote candidates from the original array, then add a value (determined by another mapped array provided somewhere else).

Once I then have grouped_last with the new values I'll sort and analyse again for last position.

The problem is that I want grouped last to be a standalone array and not affect the original array/hash.

+2  A: 

your original array is an array of hashes. find_all returns a new array, but the contents of that array are references to the original hashes. try this:

def deep_copy(obj)
  Marshal.load(Marshal.dump(obj))
end

grouped_last = votes_array.find_all {|i| i["votes"][0] == votes_array.last["votes"][0] }
grouped_last = deep_copy(grouped_last)

also, grouped_last = [] does nothing; you don't need to initialise variables that way.

Edit: this irb session should make it clearer what's going on:

>> votes_array.map {|x| x.object_id}
=> [80823078, 80822924, 80822784, 80822644, 80822504]

>> grouped_last = votes_array.find_all {|i| i["votes"][0] ==  votes_array.last["votes"][0] }
=> [{"votes"=>[3], "id"=>22, "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]

>> grouped_last.map {|x| x.object_id}
=> [80822644, 80822504]

>> grouped_last = votes_array.find_all {|i| i["votes"][0] == votes_array.last["votes"][0] }.dup

=> [{"votes"=>[3], "id"=>22, "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]
>> grouped_last.map {|x| x.object_id}

=> [80822644, 80822504]
>> grouped_last = votes_array.find_all {|i| i["votes"][0] == votes_array.last["votes"][0] }.map {|x| x.dup}

=> [{"votes"=>[3], "id"=>22, "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]
>> grouped_last.map {|x| x.object_id}
=> [81390302, 81390288]

>> grouped_last = votes_array.find_all {|i| i["votes"][0] == votes_array.last["votes"][0] }
=> [{"votes"=>[3], "id"=>22, "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]

>> grouped_last = deep_copy(grouped_last)
=> [{"votes"=>[3], "id"=>22, "elected"=>0}, {"votes"=>[3], "id"=>"0", "elected"=>0}]

>> grouped_last.map {|x| x.object_id}
=> [80366188, 80362338]
Martin DeMello
Watch out because the array for votes is not cloned and is still a reference to the original. The dup method does not "deep clone" like you might expect, but instead only makes a copy of the first layer.
tadman
oops, yes, i thought that the array contents were a flat hash, and so dup would work fine. didn't notice some of the values were themselves arrays.
Martin DeMello
Thank you Martin - that works like a charm!
Les
tadman:i've updated the script to use a deep copy
Martin DeMello