i am still learning. in the below code @attributes is a hash table. i
want to add a new value ‘a’ for a key represented by ‘a.key’. i also
want to group values with the same keys in to arrays. thanks for any
help.
konstantin
if @attributes.has_key?(a.key)
if @attributes[a.key].is_a?(Array) @attributes[a.key] << a
else @attributes[a.key] = [@attributes[a.key], a]
end
else @attributes[a.key] = a
end
def add(key, val)
if $hash.has_key?(key)
if $hash[key].is_a?(Array)
$hash[key] << val
else
$hash[key] = [$hash[key], val]
end
else
$hash[key] = val
end
end
def add2(key, val)
if $hash.has_key?(key)
$hash[key].to_a << val
else
$hash[key] = val.to_a
end
end
key = “a”
val = “aval”
add2(key, val)
puts $hash.inspect
key = “a”
val = “aval2”
add2(key, val)
puts $hash.inspect
yes, this is the point of hashes. in my program, i have attributes with
names (keys). i want to collect these attributes in to a data
structure. attributes with the same names form an array. like in a HTTP
POST form, if you have fields with the same names they are treated as
an array.
if @attributes[a.key].is_a?(Array) @attributes[a.key] << a
else @attributes[a.key] = [@attributes[a.key], a]
end
else @attributes[a.key] = a
end
This is a fairly standard Ruby idiom (a hash of arrays):
(@attributes[a.key]||=[])<<a
What the above is doing is setting the entry for a.key to an empty
array if nothing exists, otherwise it will return the array already at
that location. Then the element a is added to that array.
yes, this is the point of hashes. in my program, i have attributes with
names (keys). i want to collect these attributes in to a data
structure. attributes with the same names form an array. like in a HTTP
POST form, if you have fields with the same names they are treated as
an array.
In my opinion it is better to just put all the attributes into an
array (which is what my code does), even the single ones, that way you
can treat all the hashtable entries the same way (since they will all
be arrays.)
thank you. this assumes that the hash’s default value is nil. but this
is a good enough assumption for my purposes. i was hoping to avoid
creating one-element arrays and still have the simplicity of a single
line solution but i guess this cant be done. thanks for your help.
Personally, I would keep every element of the hash an array. You can do
that
like so…
def add4( key, value ) @hsh[key] = @hsh[key].to_a<<value
end
if you must keep entries in the hash that only have one value as single
elements and not as arrays…
def add5( key, value ) @hsh[key] = @hsh[key]?@hsh[key].to_a<<value:value
end
You could get fancier and just call “<<” on everything, catching
exceptions and handling them for objects that don’t implement
that method.
It’s better to use respond_to? – if you get a NameError, you can’t
distinguish between the object itself not responding to :“<<” and
something deep in the bowels of its :“<<” implementation not
responding to :“<<”.
you set the default valued returned by the hash to an array. The
problem is that it will always return the same array. So in the example
provided above you will end up with an empty hash for @attributes.
What you really want is the default block style:
Hash.new{|hash, key| hash[key] = []}