Forum: Ruby clean nice way (hash)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Shai R. (Guest)
on 2007-07-16 14:17
hi,
was wondering what the prettiest way to do the below would be:

i got a hash

{ '1' => 'some', '4' => 'thing', '6' => 'good' }

and i want to turn it into

{ '1' => {'name' => 'some'}, '4' => {'name' => 'thing'}, '6' =>
{'name'=>'good'} }
SonOfLilit (Guest)
on 2007-07-16 14:53
(Received via mailing list)
hash.map{|k, v| {k => {'name' => v}}.to_hash

should work.

Not sure if there's a prettier solution.

Aur
SonOfLilit (Guest)
on 2007-07-16 14:54
(Received via mailing list)
Wait, no.

hash.map{|k, v| k, {'name' => v}}.to_hash

This should work.

Or
h = {}
hash.each{|k, v| h.add(k, {'name' => 'v'})

Aur
Hemant K. (Guest)
on 2007-07-16 14:59
(Received via mailing list)
On 7/16/07, Shai R. <removed_email_address@domain.invalid> wrote:
> {'name'=>'good'} }
Dunno, I can think of a couple of ways:

a.each {|key,value| a[key] = {'name' => value}}

or

a.inject({}) {|mem,(key,value)| mem[key] = {'name' => value}; mem }



--
Let them talk of their oriental summer climes of everlasting
conservatories; give me the privilege of making my own summer with my
own coals.

http://blog.gnufied.org
SonOfLilit (Guest)
on 2007-07-16 15:17
(Received via mailing list)
On 7/16/07, hemant <removed_email_address@domain.invalid> wrote:
> > { '1' => {'name' => 'some'}, '4' => {'name' => 'thing'}, '6' =>
> > {'name'=>'good'} }
>
> Dunno, I can think of a couple of ways:
>
> a.each {|key,value| a[key] = {'name' => value}}

Are you allowed to do this?

> or
>
> a.inject({}) {|mem,(key,value)| mem[key] = {'name' => value}; mem }

Wow, thanks for showing me a new idea.


Aur
Hemant K. (Guest)
on 2007-07-16 15:52
(Received via mailing list)
On 7/16/07, SonOfLilit <removed_email_address@domain.invalid> wrote:
> > >
> > > { '1' => {'name' => 'some'}, '4' => {'name' => 'thing'}, '6' =>
> > > {'name'=>'good'} }
> >
> > Dunno, I can think of a couple of ways:
> >
> > a.each {|key,value| a[key] = {'name' => value}}
>
> Are you allowed to do this?

Why not? Seem to work here.

>
--
Let them talk of their oriental summer climes of everlasting
conservatories; give me the privilege of making my own summer with my
own coals.

http://blog.gnufied.org
SonOfLilit (Guest)
on 2007-07-16 16:05
(Received via mailing list)
> > > a.each {|key,value| a[key] = {'name' => value}}
> >
> > Are you allowed to do this?
>
> Why not? Seem to work here.
>

I was taught not to modify a structure I'm #each ing on...

Does this not apply when you only modify substructures of it?


Aur
Stefan R. (Guest)
on 2007-07-16 16:15
SonOfLilit wrote:
>> > > a.each {|key,value| a[key] = {'name' => value}}
>> >
>> > Are you allowed to do this?
>>
>> Why not? Seem to work here.
>>
>
> I was taught not to modify a structure I'm #each ing on...
>
> Does this not apply when you only modify substructures of it?
>
>
> Aur

I think that only applies for insert/delete. As e.g. with an array it
may happen that elements are left out if you delete them wile iterating,
or that you iterate over a newly create value (which you might not want
to)

Regards
Stefan
SonOfLilit (Guest)
on 2007-07-16 17:35
(Received via mailing list)
On 7/16/07, Stefan R. <removed_email_address@domain.invalid> wrote:
> > Does this not apply when you only modify substructures of it?
> Stefan
>
> --
> Posted via http://www.ruby-forum.com/.
>
>

You just made me ponder a cool thing:

N = 8
(a = [0, 1]).each_index{|n, i| a << a[i-1] + a[i] if i > 0 and i < N

Of course it probably wouldn't work, but... Cool

Aur
James G. (Guest)
on 2007-07-16 18:04
(Received via mailing list)
On Jul 16, 2007, at 5:58 AM, hemant wrote:

> a.inject({}) {|mem,(key,value)| mem[key] = {'name' => value}; mem }

I would write that as:

   a.inject(Hash.new) { |h, (k, v)| h.merge(k => v) }

James Edward G. II
Chris C. (Guest)
on 2007-07-16 18:24
(Received via mailing list)
On 7/16/07, James Edward G. II <removed_email_address@domain.invalid> wrote:
>
But that doesn't yield what he wanted... You could do
a.inject(Hash.new){|h,(k,v)| h.merge(k => { 'name' => value})}
James G. (Guest)
on 2007-07-16 18:59
(Received via mailing list)
On Jul 16, 2007, at 9:19 AM, Chris C. wrote:

>>
>>
>
> But that doesn't yield what he wanted... You could do
> a.inject(Hash.new){|h,(k,v)| h.merge(k => { 'name' => value})}

Right, my bad.  Thanks.

I just wanted to get away from using inject() like each().

James Edward G. II
Robert K. (Guest)
on 2007-07-16 18:59
(Received via mailing list)
2007/7/16, Chris C. <removed_email_address@domain.invalid>:
> >
> >
>
> But that doesn't yield what he wanted... You could do
> a.inject(Hash.new){|h,(k,v)| h.merge(k => { 'name' => value})}

Still it's inefficient because of all the small Hashes that are thrown
away immediately. The solution provided by hemant is better although
not as elegant.

Kind regards

robert
James G. (Guest)
on 2007-07-16 19:42
(Received via mailing list)
On Jul 16, 2007, at 9:36 AM, Robert K. wrote:

>> >
>> > James Edward G. II
>> >
>> >
>>
>> But that doesn't yield what he wanted... You could do
>> a.inject(Hash.new){|h,(k,v)| h.merge(k => { 'name' => value})}
>
> Still it's inefficient because of all the small Hashes that are thrown
> away immediately. The solution provided by hemant is better although
> not as elegant.

If that bothers you, change merge() to merge!().

James Edward G. II
Robert D. (Guest)
on 2007-07-16 19:50
(Received via mailing list)
On 7/16/07, SonOfLilit <removed_email_address@domain.invalid> wrote:
> Wait, no.
>
> hash.map{|k, v| k, {'name' => v}}.to_hash
>
Well that is very nice;), but where does #to_hash come from?? (Facets,
maybe?)
I suppose it should behave like

def to_hash
   Hash[*self]
end


<snip>

Robert
Florian Aßmann (Guest)
on 2007-07-16 20:00
(Received via mailing list)
Robert K. schrieb:
>> > James Edward G. II
> Kind regards
>
> robert
>
>

Hi,

I also tested a theory of mine, Hash#update does this ~20% faster than
Hash#each:

hash.update(hash) { |key, o_val, n_val| Hash['name' => o_val] }

Thread ID: 101250
Total: 7.24

 %self     total     self     wait    child    calls  name
 66.16      7.24     4.79     0.00     2.45        1  Hash#update
 33.84      2.45     2.45     0.00     0.00   456976  <Class::Hash>#[]
  0.00      7.24     0.00     0.00     7.24        0  Global#[No method]

hash.each { |key, value| hash[key] = Hash['name' => value] }

Thread ID: 101250
Total: 9.28

 %self     total     self     wait    child    calls  name
 60.78      9.28     5.64     0.00     3.64        1  Hash#each
 26.62      2.47     2.47     0.00     0.00   456976  <Class::Hash>#[]
 12.61      1.17     1.17     0.00     0.00   456976  Hash#[]=
  0.00      9.28     0.00     0.00     9.28        0  Global#[No method]

As I profiled both cases I came across something I don't understand:
When I do the profiling of both cases In the same Ruby process the later
one always is slower as when I profile them seperatly, can somebody
explain this?

Regards
Florian

profile_update.rb:
==================
require 'rubygems'
require 'ruby-prof'

puts 'Hash#update'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.update(hash) { |key, o_val, n_val| Hash['name' => o_val] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)


profile_update_first.rb:
========================
require 'rubygems'
require 'ruby-prof'

puts 'Hash#update'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.update(hash) { |key, o_val, n_val| Hash['name' => o_val] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)

puts 'Hash#each'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.each { |key, value| hash[key] = Hash['name' => value] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)


profile_each.rb:
================
require 'rubygems'
require 'ruby-prof'

puts 'Hash#each'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.each { |key, value| hash[key] = Hash['name' => value] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)


profile_each_first.rb:
======================
require 'rubygems'
require 'ruby-prof'

puts 'Hash#each'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.each { |key, value| hash[key] = Hash['name' => value] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)

puts 'Hash#update'

hash = ('aaaa'..'zzzz').inject Hash.new do |hash, value|
  hash[value] = value
  hash
end

result = RubyProf.profile do
  hash.update(hash) { |key, o_val, n_val| Hash['name' => o_val] }
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
Robert K. (Guest)
on 2007-07-16 21:12
(Received via mailing list)
On 16.07.2007 17:03, James Edward G. II wrote:
>>> >    a.inject(Hash.new) { |h, (k, v)| h.merge(k => v) }
>> not as elegant.
>
> If that bothers you, change merge() to merge!().

... which doesn't help because it does not avoid all those one pair
hashes. :-)

Kind regards

  robert
Robert K. (Guest)
on 2007-07-16 21:13
(Received via mailing list)
On 16.07.2007 17:41, Florian Aßmann wrote:
>>>> James Edward G. II
>> robert
> Thread ID: 101250
> Total: 9.28
> explain this?
I'd guess it's GC.  IMHO for comparing performance Benchmark is a better
tool - especially since you can have a warmup run.

Kind regards

  robert
James G. (Guest)
on 2007-07-16 22:16
(Received via mailing list)
On Jul 16, 2007, at 12:10 PM, Robert K. wrote:

>>>> >
>>> thrown
>>> away immediately. The solution provided by hemant is better although
>>> not as elegant.
>> If that bothers you, change merge() to merge!().
>
> ... which doesn't help because it does not avoid all those one pair
> hashes. :-)

I did eliminate some Hashes, those created for each iteration inject
(), so I feel it's wrong to say it didn't help.  You are right about
the parameter Hashes though.

I do feel nitpicking something like this is squarely in the domain of
premature optimization though.  It's GC's job to lose sleep over the
objects I create, not mine.  I don't need to get involved until it
looks like GC is getting behind.  Those are my feelings on the matter.

James Edward G. II
Martin DeMello (Guest)
on 2007-07-16 23:17
(Received via mailing list)
On 7/16/07, SonOfLilit <removed_email_address@domain.invalid> wrote:
> > > > a.each {|key,value| a[key] = {'name' => value}}
> > >
> > > Are you allowed to do this?
> >
> > Why not? Seem to work here.
> >
>
> I was taught not to modify a structure I'm #each ing on...
>
> Does this not apply when you only modify substructures of it?

It doesn't affect the "spine" of the structure, just the values hanging
off it.

martin
Stefan R. (Guest)
on 2007-07-16 23:40
Florian Aßmann wrote:
> hash.update(hash) { |key, o_val, n_val| Hash['name' => o_val] }

Hash[foo => bar] is a needless method call. foo => bar is already a hash
literal, Hash::[] creates a new hash from that hash (suprised me a bit
tbh, as I don't really see a reason for it to do that), though. But
since that's useless work and method calls weigh heavy in ruby, I'd not
do that.

You can try it if you want:
x = {:a => :b}
Hash[x].equal?(x) # => false

Regards
Stefan
Florian Aßmann (Guest)
on 2007-07-17 00:42
(Received via mailing list)
Ahh, ok, till now I thought {} calls Hash[] but I didn't know what
literals really are...

Thanks
Florian
Florian Aßmann (Guest)
on 2007-07-17 01:25
(Received via mailing list)
Ok, I think GC is the main problem in my setup :D

I tried benchmark and #each behaves much better than #update. #update
produces too much overhead... :(

The source below can kill your memory since I disabled the GC and only
call it manually...

Regards
Florian


require 'benchmark'

Benchmark.bm do |x|
  GC.disable

  tests = [
    Proc.new do
      hash = ('aaaa'..'zzzz').inject Hash.new do |mem, value|
        mem[value] = value
        mem
      end
      block = proc { |key, o_val, n_val| {:name => o_val} }
      x.report('Hash#update') { hash.update(hash, &block) }
    end ] + [
    Proc.new do
      hash = ('aaaa'..'zzzz').inject Hash.new do |mem, value|
        mem[value] = value
        mem
      end
      block = proc { |key, value| hash[key] = {:name => value} }
      x.report('Hash#each') { hash.each(&block) }
    end
  ]

  12.times do |iteration|
    if (iteration % 3).zero?
      GC.start
      sleep 3
    else
      tests[ rand(2) ].call
    end
  end

end
GC.enable
sleep 5
Robert K. (Guest)
on 2007-07-17 01:28
(Received via mailing list)
On 16.07.2007 20:15, James Edward G. II wrote:
>>>>> >
>>>>
> parameter Hashes though.
Those were the ones I referred to originally.  And yes, "did not help"
was only partly true. ;-)

> I do feel nitpicking something like this is squarely in the domain of
> premature optimization though.  It's GC's job to lose sleep over the
> objects I create, not mine.  I don't need to get involved until it looks
> like GC is getting behind.  Those are my feelings on the matter.

I think I understand your point and as a geneal statement I even do
agree.  However, another general statement that I'd add to the mix is
"do not make superfluous work if you can avoid it easily".  For me this
is such a case: we just get a slightly more elegant solution by using
#merge because we do not need to mention the Hash to make #inject happy,
but we force the machine to do a lot of extra work (how much remains to
be measured).  It's not exactly a comparable case but if I have a tree
where data is stored with a particular order I would not do a full
traversal when searching for a specific element (O(n)); instead I'd use
an algorith that's aware of the tree structure and the ordering
(O(log(n))).

Kind regards

  robert
This topic is locked and can not be replied to.