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.
4c3acdc3d93f54cc7a7281780ec8a4ee?d=identicon&s=25 Shai Rosenfeld (shai)
on 2007-07-16 12: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'} }
7b4707f974af261f71943e1f2046c9ee?d=identicon&s=25 SonOfLilit (Guest)
on 2007-07-16 12: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
7b4707f974af261f71943e1f2046c9ee?d=identicon&s=25 SonOfLilit (Guest)
on 2007-07-16 12: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
87e41d0d468ad56a3b07d9a6482fd6d5?d=identicon&s=25 Hemant Kumar (gnufied)
on 2007-07-16 12:59
(Received via mailing list)
On 7/16/07, Shai Rosenfeld <shaiguitar@gmail.com> 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
7b4707f974af261f71943e1f2046c9ee?d=identicon&s=25 SonOfLilit (Guest)
on 2007-07-16 13:17
(Received via mailing list)
On 7/16/07, hemant <gethemant@gmail.com> 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
87e41d0d468ad56a3b07d9a6482fd6d5?d=identicon&s=25 Hemant Kumar (gnufied)
on 2007-07-16 13:52
(Received via mailing list)
On 7/16/07, SonOfLilit <sonoflilit@gmail.com> 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
7b4707f974af261f71943e1f2046c9ee?d=identicon&s=25 SonOfLilit (Guest)
on 2007-07-16 14: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
Cf7cd97cdc8ed7d4ae92965b24f0dfad?d=identicon&s=25 Stefan Rusterholz (apeiros)
on 2007-07-16 14: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
7b4707f974af261f71943e1f2046c9ee?d=identicon&s=25 SonOfLilit (Guest)
on 2007-07-16 15:35
(Received via mailing list)
On 7/16/07, Stefan Rusterholz <apeiros@gmx.net> 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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-07-16 16: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 Gray II
96931bfe0c2948f47a98e15ae52e5637?d=identicon&s=25 Chris Carter (cdcarter)
on 2007-07-16 16:24
(Received via mailing list)
On 7/16/07, James Edward Gray II <james@grayproductions.net> wrote:
>
But that doesn't yield what he wanted... You could do
a.inject(Hash.new){|h,(k,v)| h.merge(k => { 'name' => value})}
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-07-16 16:59
(Received via mailing list)
On Jul 16, 2007, at 9:19 AM, Chris Carter 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 Gray II
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-16 16:59
(Received via mailing list)
2007/7/16, Chris Carter <cdcarter@gmail.com>:
> >
> >
>
> 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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-07-16 17:42
(Received via mailing list)
On Jul 16, 2007, at 9:36 AM, Robert Klemme wrote:

>> >
>> > James Edward Gray 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 Gray II
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-07-16 17:50
(Received via mailing list)
On 7/16/07, SonOfLilit <sonoflilit@gmail.com> 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
Ee469623eb1b8e6e35d192822b9c4aa2?d=identicon&s=25 Florian Aßmann (Guest)
on 2007-07-16 18:00
(Received via mailing list)
Robert Klemme schrieb:
>> > James Edward Gray 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)
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-16 19:12
(Received via mailing list)
On 16.07.2007 17:03, James Edward Gray 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
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-16 19:13
(Received via mailing list)
On 16.07.2007 17:41, Florian Aßmann wrote:
>>>> James Edward Gray 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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-07-16 20:16
(Received via mailing list)
On Jul 16, 2007, at 12:10 PM, Robert Klemme 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 Gray II
Ae16cb4f6d78e485b04ce1e821592ae5?d=identicon&s=25 Martin DeMello (Guest)
on 2007-07-16 21:17
(Received via mailing list)
On 7/16/07, SonOfLilit <sonoflilit@gmail.com> 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
Cf7cd97cdc8ed7d4ae92965b24f0dfad?d=identicon&s=25 Stefan Rusterholz (apeiros)
on 2007-07-16 21: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
Ee469623eb1b8e6e35d192822b9c4aa2?d=identicon&s=25 Florian Aßmann (Guest)
on 2007-07-16 22:42
(Received via mailing list)
Ahh, ok, till now I thought {} calls Hash[] but I didn't know what
literals really are...

Thanks
Florian
Ee469623eb1b8e6e35d192822b9c4aa2?d=identicon&s=25 Florian Aßmann (Guest)
on 2007-07-16 23: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
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-07-16 23:28
(Received via mailing list)
On 16.07.2007 20:15, James Edward Gray 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.