Reputation: 2609
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?
Upvotes: 0
Views: 74
Reputation: 35298
When you do this:
clubs_to_play = all_clubs = Club.all
What you're basically doing is this:
all_clubs = Club.all
clubs_to_play = all_clubs
They are the same object (which you will see if you look at their object_id
s).
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 both all_clubs
and clubs_to_play
, since they are the exact same object.
Conversely, when you do this:
all_clubs = Club.all
clubs_to_play = Club.all
Here all_clubs
and clubs_to_play
reference different objects. That is to say, they have different object_ids
and when you iterate all_clubs
all of its records are materialized, but clubs_to_play
remains unaffected until you iterate it (and delete values from the other collection).
Since your algorithm is assuming that all_clubs
and clubs_to_play
can 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:
Club.all.combination(2).each_with_index do |(club1, club2), idx|
Fixture.create(
:home => idx.even? ? club1 : club2,
:away => idx.even? ? club2 : club1
)
end
There are technically more functional ways to maintain the alternation between home and away, but they are more convoluted. Enumerable#combination
returns all possible (unique) combinations of values in the collection.
Upvotes: 1