Initializing hashes with custom values

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,564
Reaction score
3,846
First Language
English
Suppose you had some code like this: you have a hash that you want to add data to throughout your game.

module TH @@myData = {} def self.add_data(key, val) @@myData[key] ||= [] @@myData[key].push(val) end def self.myData @@myData endendTH.add_data(2, 3)p TH.myDataWhich is perfectly sensible if each key in your hash points to an array, but the keys are dynamically inserted into the hash so you are not guaranteed that the key you want actually exists.However, Ruby hashes are pretty nice.

You can declare it like this

module TH @@myData = Hash.new {|hash, key| hash[key] = [] } def self.add_data(key, val) @@myData[key].push(val) end def self.myData @@myData endendTH.add_data(2, 3)p TH.myDataAnd now whenever you try to access a key that is not in the hash, it will automatically run the code in that block (in this case, set it to empty array).This saves you from having to write ugly things like the explicit initialization code from the first example.
 
Last edited by a moderator:

MobiusXVI

Game Maker
Veteran
Joined
Mar 20, 2013
Messages
383
Reaction score
91
First Language
English
Primarily Uses
You could also just do:

@@myData = Hash.new( [] )

Which would set the default value of the hash to an empty array. The only difference between this and your version is that this method wouldn't store the invalid key as a new key with an empty array value. Which may/may not be something you desire.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
I think this came out of a discussion we were having in a script development thread about how to force a hash to create a new key with an empty array value, if the key being used does not already exist, without having to do a hash.has_key? every single time.

It's so I can say

Code:
hash[key].push(value)
instead of having to say
Code:
hash[key] = [] if !hash.has_key?(key)hash[key].push(value)
I did use it Hime, so thanks :)
 
Last edited by a moderator:

Zeriab

Huggins!
Veteran
Joined
Mar 20, 2012
Messages
1,268
Reaction score
1,422
First Language
English
Primarily Uses
RMXP
You could also just do:

@@myData = Hash.new( [] )

Which would set the default value of the hash to an empty array. The only difference between this and your version is that this method wouldn't store the invalid key as a new key with an empty array value. Which may/may not be something you desire.
Oh no, the difference is greater than that.

When you give a default object it will use that default object. You are probably shooting yourself in the foot by given an array as the default value rather than having a block execute on missing keys.

@my_hash = Hash.new([])@my_hash[2] << 2@my_hash[:horse] << "Foobar"msgbox_p @my_hash[2]msgbox_p @my_hash[:horse]
Hash.new(default_object) is absolutely great for immutable objects. It does not matter that all default values point to the same object because you cannot actually change it.

Consider Hash.new(42) for example. To change it you don't do 42.change_internal_value but rather create a new number.

When giving a defaulting block you do not have to provide a static value. It is perfectly possible to define infinite sequences since actual values are only evaluated when needed:

@my_hash = Hash.new {|hash, key| hash[key] = key**2 }msgbox_p @my_hash[1]msgbox_p @my_hash[2]msgbox_p @my_hash[-42]msgbox_p @my_hash[600]msgbox_p @my_hash[:horse] # Crash
*hugs*

 - Zeriab
 
Last edited by a moderator:

MobiusXVI

Game Maker
Veteran
Joined
Mar 20, 2013
Messages
383
Reaction score
91
First Language
English
Primarily Uses
Oh no, the difference is greater than that.

When you give a default object it will use that default object. You are probably shooting yourself in the foot by given an array as the default value rather than having a block execute on missing keys.

@my_hash = Hash.new([])@my_hash[2] << 2@my_hash[:horse] << "Foobar"msgbox_p @my_hash[2]msgbox_p @my_hash[:horse]Hash.new(default_object) is absolutely great for immutable objects. It does not matter that all default values point to the same object because you cannot actually change it.

Consider Hash.new(42) for example. To change it you don't do 42.change_internal_value but rather create a new number.

When giving a defaulting block you do not have to provide a static value. It is perfectly possible to define infinite sequences since actual values are only evaluated when needed:

@my_hash = Hash.new {|hash, key| hash[key] = key**2 }msgbox_p @my_hash[1]msgbox_p @my_hash[2]msgbox_p @my_hash[-42]msgbox_p @my_hash[600]msgbox_p @my_hash[:horse] # Crash
I just meant in this instance, the block that was provided didn't do much more than return the same default value for every key that wasn't already set up in the hash. Obviously blocks let you be more dynamic with how you handle keys that aren't included in the hash, but sometimes you don't need that, and I wanted to point out the alternative.
 

Zeriab

Huggins!
Veteran
Joined
Mar 20, 2012
Messages
1,268
Reaction score
1,422
First Language
English
Primarily Uses
RMXP
Also knowing about creating a Hash with a default object is valuable, but in this specific case it is fundamentally different.

Try it out. The following code clearly illustrates the danger of your approach in this instance.

# Default value@my_hash = Hash.new([])@my_hash[2] << 2@my_hash[:horse] << "Foobar"msgbox_p @my_hash[2]msgbox_p @my_hash[:horse]# Defauling function@my_hash = Hash.new {|hash, key| hash[key] = []}@my_hash[2] << 2@my_hash[:horse] << "Foobar"msgbox_p @my_hash[2]msgbox_p @my_hash[:horse]*hugs*
 
Last edited by a moderator:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,564
Reaction score
3,846
First Language
English
I just meant in this instance, the block that was provided didn't do much more than return the same default value for every key that wasn't already set up in the hash. Obviously blocks let you be more dynamic with how you handle keys that aren't included in the hash, but sometimes you don't need that, and I wanted to point out the alternative.
I probably should have written down the purpose of this topic, but basically it's to have a separate array of elements for each key (the arrays could contain the same values, but that's just a coincidence).

Assigning the default value as an array has the (possibly unforeseen) consequence that all hash values simply point to the same array, as illustrated above. This would break the requirement that all keys point to their own values.

Now if I were working with integer values instead of array values, and all keys should begin with 0, THEN I would consider just using

@my_data = Hash.new(0)as that is not vulnerable to the problems that an array (or probably any mutable object) has.But I would be careful not to mix the two together and apply it to all situations.
 
Last edited by a moderator:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,564
Reaction score
3,846
First Language
English
Recently I ran into a bug where I couldn't save and was wondering why.

Turns out one of the scripts I was using was doing the Hash.new method as described above.

However, Ruby uses a proc to manage the hash, which makes sense when you think about it.

The problem occurs when you try to marshal dump it: marshal doesn't allow you to serialize procs.

So for example, the following code would fail

class Game_Actor alias :old_init :initialize def initialize(actor_id) old_init(actor_id) @arg = Hash.new { } endendSimply because that creates a proc.So, while this is a nice way to initialize things, if you need to store it in the save file, it would be better to just stick with explicit checks.
 
Last edited by a moderator:

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Profile Posts

so hopefully tomorrow i get to go home from the hospital i've been here for 5 days already and it's driving me mad. I miss my family like crazy but at least I get to use my own toiletries and my own clothes. My mom is coming to visit soon i can't wait to see her cause i miss her the most. :kaojoy:
Couple hours of work. Might use in my game as a secret find or something. Not sure. Fancy though no? :D
Holy stink, where have I been? Well, I started my temporary job this week. So less time to spend on game design... :(
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.
Do you Find Tilesetting or Looking for Tilesets/Plugins more fun? Personally I like making my tileset for my Game (Cretaceous Park TM) xD

Forum statistics

Threads
105,868
Messages
1,017,070
Members
137,577
Latest member
SadaSoda
Top