class Zone
property map_id = 0
end
class Game
property zones = Hash(Int32, Zone).new
def initialize(zone_id)
zones[zone_id] = Zone.new
end
end
games = Hash(String, Game).new
10.times do |i|
games["random_gamename#{i}"] = Game.new(i)
end
zones = games.select(Game).select(Zone)
pp games.select { |_, v| v.is_a? Game}
pp games["random_gamename1"].is_a? Game
Hmm, it’s true. Why doesn’t it select and return an array of all the Game elements in the Hash?
I feel like I’m getting closer :p
edit: Actually, I don’t even know if this is the correct way for what I am wanting to do.
I basically just want to save an each block (calling .each on games), and just iterate over the zones. I don’t like nested .each blocks, they get messy.
I’m not entirely sure what’s going on with Hash(K,V).select, but it looks like its splat signature is trying to process the class as a key, which obviously doesn’t work. Maybe someone with more Crystal experience than I have can make it work with something like games.select(Tuple(String, Game)), but it doesn’t look like that’s really what you want anyway.
Are you trying to call a method on each zone? If so, I suggest you add a method on Game that calls that method on each of its Zones. However, if you clarified exactly what you’re looking to do, it would help us help you.
def grab_zones(a)
temp_zones = Array(Zone).new
a.each do |k, v|
v.zones.each do |k2, v2|
temp_zones << v2
end
end
temp_zones
end
zones = grab_zones(games)
Or…
def grab_zones(a)
temp_zones = Hash(Int32, Zone).new
a.each do |k, v|
v.zones.each do |k2, v2|
temp_zones[k2] = v2
end
end
temp_zones
end
Yeah, I just want to iterate of the zones while not having to iterate over them while I’m already inside a games.each block. In my main codebase, I do a lot of cleanup for items and entities (which are children of zones). I don’t like nested .each blocks, I wish there was a guard clause style alternative
If the conditions for cleaning up items and entities within a Zone don’t depend on the other zones, then you can probably create a #cleanup method (or something similar) on Zone and a method like
class Game
def clean_zones
zones.each &.cleanup
end
end
Hmm, I would have to create a cleanup method on my class Zone. And also another method clean_zones on my Game class. And then also call clean_zones every xx seconds. I’d rather just do this all in one place (for example, I use a separate file, CleanupTimers.cr)
class Zone
property map_id = 0
end
class Game
alias ZoneMap = Hash(Int32, Zone)
property zones = ZoneMap.new
def initialize(zone_id)
zones[zone_id] = Zone.new
end
end
games = Hash(String, Game).new
10.times do |i|
games["random_gamename#{i}"] = Game.new(i)
end
game_zones = games.flat_map { |_name, game| game.zones }
zones = game_zones.each_with_object(Game::ZoneMap.new) do |zones, zone_map|
zones.each do |id, zone|
zone_map[id] = zone
end
end
pp zones
I want to iterate over the zonesHash property without actually being inside the games.each block.
Similar to how we can do guard clauses (and not have to be inside really deep if statements. But in this case, it’s not having to be inside .each blocks.
For example:
games.each do |game_name, game_obj|
game_obj.zones.each do |zone_id, zone_obj|
eww i'm inside a 2 level deep block
zone_obj.loot.each do |itemid, item|
eww i'm now inside a 3 level deep block!
end
end
end
I could just do:
zones = games.select(Game).select(Zone)
zones.each do |zone_id, zone_obj|
Yay, now I only need to be inside one each block!
end
Would be super neat
@jgaskins’s solution does work though, so it’s not a deal-breaker. Just brainstorming outloud