I have a script that creates a soccer fixture.
Each club can play just one game with the other clubs and it could be at home or away.
This is my actual code which is not working with 20 clubs (It’s creating 145 matches instead of 190 and the clubs are not playing the same amount of games)
clubs_to_play = all_clubs = Club.all #problematic line
all_clubs.each do |club|
home = true
clubs_to_play.delete(club)
clubs_to_play.each do |club_to_play|
f = Fixture.new
if (home)
f.home = club
f.away = club_to_play
home = false
else
f.home = club_to_play
f.away = club
home = true
end
f.save
end
end
end
But if I change the first line to be:
clubs_to_play = Club.all
all_clubs = Club.all
The script works and generates 190 matches. Why is that?
When you do this:
What you’re basically doing is this:
They are the same object (which you will see if you look at their
object_ids).DataMapper does not materialize record sets until a kicker method is invoked. That basically means no SQL is executed until you start iterating. Of course, since these variables both point to the same object, when you start iterating
all_clubs, you pull all the records from the database in bothall_clubsandclubs_to_play, since they are the exact same object.Conversely, when you do this:
Here
all_clubsandclubs_to_playreference different objects. That is to say, they have differentobject_idsand when you iterateall_clubsall of its records are materialized, butclubs_to_playremains unaffected until you iterate it (and delete values from the other collection).Since your algorithm is assuming that
all_clubsandclubs_to_playcan be modified independently of one other, you are getting undesired behaviour when they are the same object.Try something like this (slightly more functional) approach:
There are technically more functional ways to maintain the alternation between home and away, but they are more convoluted.
Enumerable#combinationreturns all possible (unique) combinations of values in the collection.