What is the ruby way to do this?

hello,

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

sorry, forgot to phrase the question. the question is is there a way to
make this code more compact and pretty?

Pardon my question, but isn't the whole point of hashes that every key is
unique?

···

On 11/22/05, ako... <akonsu@gmail.com> wrote:

hello,

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

$hash = {}

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

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.

Ryan

···

On 11/22/05, ako... <akonsu@gmail.com> wrote:

hello,

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

Hi,

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.

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

An alternate approach might be:

@attributes = Hash.new {|h,k| h[k] = Array.new}

This will automatically create arrays to hold your keys, on demand.

Now you can just say:

@attributes["whatever"] << "something"
@attributes["foo"] << 123
@attributes["foo"] << 456

@attributes.inspect
{"foo"=>[123, 456], "whatever"=>["something"]}

Hope this helps,

Regards,

Bill

···

From: "ako..." <akonsu@gmail.com>

Hopefully there isn't anyone who isn't!

···

On 11/22/05, ako... <akonsu@gmail.com> wrote:

hello,

i am still learning.

and this monstrosity, which isn't in the spirit of ruby... :slight_smile:

def add3(key, val)
$hash[key] = ( $hash[key] == nil ) ? val.to_a : $hash[key] << val
end

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.

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.

konstantin

thanks. this form of Hash creation with blocks is not documented in
http://www.rubycentral.com/book/ref_c_hash.html. would you point me to
the documentation for this?

Bill Kelly wrote:

An alternate approach might be:

@attributes = Hash.new {|h,k| h[k] = Array.new}

This will automatically create arrays to hold your keys, on demand.

Now you can just say:

@attributes["whatever"] << "something"
@attributes["foo"] << 123
@attributes["foo"] << 456

@attributes.inspect
{"foo"=>[123, 456], "whatever"=>["something"]}

And, mixing some of the approaches shown:

@attributes = {}

# Assumes the given object quacks on 'key'
def @attributes.<<( keyed_obj )
   (self[keyed_obj.key]||=)<<keyed_obj
end

@attributes << a1
@attributes << a2
@attributes << a3

James

···

--

http://www.ruby-doc.org - Ruby Help & Documentation
Ruby Code & Style - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools

I would do

a = Hash.new { || [] }

def a.addValue( valueHash )
  valueHash.each_pair { |key,value| self[ key ] <<= value }
end

a.addValue "blah" => "a"
a.addValue "blah" => "b"
a.addValue "blah" => "c"

I kinda like the look of the arrows :wink:

j.

···

--
"Remember. Understand. Believe. Yield! -> http://ruby-lang.org"

Jeff Wood

Hey, thanks for that! I had no idea that the block form of Hash creation had block arguments. Sure it's in 'ri', but who looks there? :wink:

I often see people suggesting:
Hash.new{ }
without realizing that this doesn't set the value on the hash, just returns a blank array. I've never been able to use the block constructor syntax because it was so useless (because I also didn't know that <<= was a valid compound method).

foo = Hash.new{ }
foo[:bar] << 1
foo[:foo] << 2
foo[:bar] << 3
p foo
#=>{}

foo = Hash.new{ |h,v| h[v]= }
foo[:bar] << 1
foo[:foo] << 2
foo[:bar] << 3
p foo
#=>{:bar=>[1, 3], :foo=>[2]}

foo = Hash.new{ }
foo[:bar] <<= 1
foo[:foo] <<= 2
foo[:bar] <<= 3
p foo
#=>{:bar=>[1, 3], :foo=>[2]}

···

On Nov 22, 2005, at 5:45 PM, Bill Kelly wrote:

@attributes = Hash.new {|h,k| h[k] = Array.new}

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.)

Ryan

···

On 11/22/05, ako... <akonsu@gmail.com> wrote:

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.

And finally I get to

def add4(key, val)
$hash[key]= $hash[key].to_a << val
end

These mean your values are allways arrays, but thats no problem right?

Konstantin,

When you have a single attribute associated with a key, is it
acceptable to have it in a one-element array? If so, you could do
something like:

@attributes = {}
....
@attributes[a.key] ||= []
@attributes[a.key] << a

Or, if you don't mind having empty keys return an empty array instead
of nil, you could do:

@attributes = Hash.new([])
....
@attributes[a.key] << a

Cheers,
Ken

Note that a "Ruby" hint would be to get rid of your "is_a?", preferring to use "respond_to?" instead.

You could get fancier and just call "<<" on everything, catching exceptions and handling them for objects that don't implement that method.

Caleb

···

On Nov 22, 2005, at 5:42 PM, ako... wrote:

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.

konstantin

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

ako... wrote:

thanks. this form of Hash creation with blocks is not documented in
http://www.rubycentral.com/book/ref_c_hash.html\. would you point me to
the documentation for this?

See ri Hash.new.

Regards,
Matthew