Hash with two identical keys?

Discussion in 'Ruby' started by Trans, Dec 26, 2006.

  1. Trans

    Trans Guest

    >From Facets' multiton.rb (which is primarily Floran Franks' work), I'm
    getting somthing that doesn't make any sense:

    POOLS[self] ||= {}
    p POOLS[self].class
    p POOLS[self].keys

    Is outputing:

    Hash
    [[{:strip_comments=>false}], [{:strip_comments=>false}]]

    How can two identical keys be in a hash?

    T.
    Trans, Dec 26, 2006
    #1
    1. Advertising

  2. Trans

    Alex LeDonne Guest

    On 12/26/06, Trans <> wrote:
    > >From Facets' multiton.rb (which is primarily Floran Franks' work), I'm

    > getting somthing that doesn't make any sense:
    >
    > POOLS[self] ||= {}
    > p POOLS[self].class
    > p POOLS[self].keys
    >
    > Is outputing:
    >
    > Hash
    > [[{:strip_comments=>false}], [{:strip_comments=>false}]]
    >
    > How can two identical keys be in a hash?
    >
    > T.
    >

    irb(main):005:0> bob = [{:whee => false}]
    => [{:whee=>false}]
    irb(main):006:0> bob.hash
    => 23244868
    irb(main):007:0> roger = [{:whee => false}]
    => [{:whee=>false}]
    irb(main):008:0> roger.hash
    => 23235888
    irb(main):009:0> bob.eql? roger
    => false
    irb(main):010:0> bob[0].eql? roger[0]
    => false
    irb(main):011:0> bob[0].hash
    => 23244870
    irb(main):012:0> roger[0].hash
    => 23235890

    I think it boils down to: there is no Hash#hash which inspects the
    contents - Hash inherits Object#hash which is Object#object_id, and
    the two hashes, while they appear to have the same contents, are not
    the same object. So the keys are not "identical".

    -A
    Alex LeDonne, Dec 26, 2006
    #2
    1. Advertising

  3. Trans

    Jon Garvin Guest

    Looks to me that your keys are arrays.

    My guess is that

    p POOLS[self].keys[0].class

    will return Array, instead of Symbol, which is what you're probably
    expecting. So, the hash doesn't have identical keys. It has two
    different keys, each of which is a different array with identical
    members. Try

    p POOLS[self].keys[0].object_id
    p POOLS[self].keys[1].object_id

    and you'll see the keys really are different.

    Trans wrote:
    > >From Facets' multiton.rb (which is primarily Floran Franks' work), I'm

    > getting somthing that doesn't make any sense:
    >
    > POOLS[self] ||= {}
    > p POOLS[self].class
    > p POOLS[self].keys
    >
    > Is outputing:
    >
    > Hash
    > [[{:strip_comments=>false}], [{:strip_comments=>false}]]
    >
    > How can two identical keys be in a hash?
    >
    > T.
    >
    >
    >
    >
    Jon Garvin, Dec 26, 2006
    #3
  4. Trans

    Trans Guest

    thanks Ara and Jon,

    I see what your saying. I was using #== not #eql? in comparing the
    keys. So I see why it's faling now. How do I get aorund this? I'm
    caching object based on therr initialization paramaters, which has to
    be an array. Is there a simpler way or do I have to do something like:

    class Parameters < Array
    alias :eql? :==
    end

    T.
    Trans, Dec 26, 2006
    #4
  5. Trans

    Trans Guest

    Trans wrote:
    > thanks Ara and Jon,
    >
    > I see what your saying. I was using #== not #eql? in comparing the
    > keys. So I see why it's faling now. How do I get aorund this? I'm
    > caching object based on therr initialization paramaters, which has to
    > be an array. Is there a simpler way or do I have to do something like:
    >
    > class Parameters < Array
    > alias :eql? :==
    > end


    Ugh. Nothing like that works either. It's not using eql? or equal?, but
    rather #hash (I guess that's actually what you were trying to say Ara).
    This doen't make any sense to me. Why do identical strings and arrays
    have the same #hash value but not hashes?

    T.
    Trans, Dec 26, 2006
    #5
  6. Trans wrote:
    > thanks Ara and Jon,
    >
    > I see what your saying. I was using #== not #eql? in comparing the
    > keys. So I see why it's faling now. How do I get aorund this? I'm
    > caching object based on therr initialization paramaters, which has to
    > be an array. Is there a simpler way or do I have to do something like:
    >
    > class Parameters < Array
    > alias :eql? :==
    > end
    >
    > T.


    How about association lists?

    irb(main):029:0> a1=[[:strip,false]]
    => [[:strip, false]]
    irb(main):030:0> a2=[[:strip,false]]
    => [[:strip, false]]
    irb(main):031:0> h={}
    => {}
    irb(main):032:0> h[a1] = 'foo'
    => "foo"
    irb(main):033:0> h[a2] = 'bar'
    => "bar"
    irb(main):034:0> h
    => {[[:strip, false]]=>"bar"}
    William James, Dec 26, 2006
    #6
  7. Trans

    Eric Hodel Guest

    On Dec 26, 2006, at 11:22, Trans wrote:
    > Trans wrote:
    >> thanks Ara and Jon,
    >>
    >> I see what your saying. I was using #== not #eql? in comparing the
    >> keys. So I see why it's faling now. How do I get aorund this? I'm
    >> caching object based on therr initialization paramaters, which has to
    >> be an array. Is there a simpler way or do I have to do something
    >> like:
    >>
    >> class Parameters < Array
    >> alias :eql? :==
    >> end

    >
    > Ugh. Nothing like that works either. It's not using eql? or equal?,
    > but
    > rather #hash (I guess that's actually what you were trying to say
    > Ara).


    A Hash only asks if one object is #eql? to another when they have the
    same #hash. You can't use a Hash as a Hash key because Hash#hash is
    not implemented that way.

    > This doen't make any sense to me. Why do identical strings and arrays
    > have the same #hash value but not hashes?


    Likely because:

    s = ''
    s.hash

    and:

    a = []
    a << a
    a.hash

    are easier to compute than:

    h = {}
    h[h] = h
    h.hash

    --
    Eric Hodel - - http://blog.segment7.net

    I LIT YOUR GEM ON FIRE!
    Eric Hodel, Dec 26, 2006
    #7
  8. Trans

    Trans Guest

    Trans wrote:
    > Trans wrote:
    > > thanks Ara and Jon,
    > >
    > > I see what your saying. I was using #== not #eql? in comparing the
    > > keys. So I see why it's faling now. How do I get aorund this? I'm
    > > caching object based on therr initialization paramaters, which has to
    > > be an array. Is there a simpler way or do I have to do something like:
    > >
    > > class Parameters < Array
    > > alias :eql? :==
    > > end

    >
    > Ugh. Nothing like that works either. It's not using eql? or equal?, but
    > rather #hash (I guess that's actually what you were trying to say Ara).
    > This doen't make any sense to me. Why do identical strings and arrays
    > have the same #hash value but not hashes?


    Nope. It doesn't even use #hash. So it must be using #object_id with an
    exception for Strings and Arrays. Dissapointing to say the least.

    I had to resort to recursively converting all hashes to arrays.

    T.
    Trans, Dec 26, 2006
    #8
  9. Trans

    Trans Guest

    Eric Hodel wrote:
    > On Dec 26, 2006, at 11:22, Trans wrote:
    > > Trans wrote:
    > >> thanks Ara and Jon,
    > >>
    > >> I see what your saying. I was using #== not #eql? in comparing the
    > >> keys. So I see why it's faling now. How do I get aorund this? I'm
    > >> caching object based on therr initialization paramaters, which has to
    > >> be an array. Is there a simpler way or do I have to do something
    > >> like:
    > >>
    > >> class Parameters < Array
    > >> alias :eql? :==
    > >> end

    > >
    > > Ugh. Nothing like that works either. It's not using eql? or equal?,
    > > but
    > > rather #hash (I guess that's actually what you were trying to say
    > > Ara).

    >
    > A Hash only asks if one object is #eql? to another when they have the
    > same #hash. You can't use a Hash as a Hash key because Hash#hash is
    > not implemented that way.


    I see. So it's not using object_id but

    a.hash == b.hash && a.eql?(b)

    Is that right?

    > > This doen't make any sense to me. Why do identical strings and arrays
    > > have the same #hash value but not hashes?

    >
    > Likely because:
    >
    > s = ''
    > s.hash
    >
    > and:
    >
    > a = []
    > a << a
    > a.hash
    >
    > are easier to compute than:
    >
    > h = {}
    > h[h] = h
    > h.hash


    Hmm... the expection gums up the works.

    T.
    Trans, Dec 26, 2006
    #9
  10. Trans

    Trans Guest

    William James wrote:
    > Trans wrote:
    > > thanks Ara and Jon,
    > >
    > > I see what your saying. I was using #== not #eql? in comparing the
    > > keys. So I see why it's faling now. How do I get aorund this? I'm
    > > caching object based on therr initialization paramaters, which has to
    > > be an array. Is there a simpler way or do I have to do something like:
    > >
    > > class Parameters < Array
    > > alias :eql? :==
    > > end
    > >
    > > T.

    >
    > How about association lists?


    Thanks William! That's what I did and worked (desipite inefficency).

    T.
    Trans, Dec 26, 2006
    #10
  11. Trans

    Trans Guest

    Trans wrote:
    > thanks Ara and Jon,


    Oops. Just saw that was Alex, not Ara, sorry about that Alex! And
    thanks for the help.

    T.
    Trans, Dec 26, 2006
    #11
  12. Trans

    Guest

    On Wed, 27 Dec 2006, Trans wrote:

    >> From Facets' multiton.rb (which is primarily Floran Franks' work), I'm

    > getting somthing that doesn't make any sense:
    >
    > POOLS[self] ||= {}
    > p POOLS[self].class
    > p POOLS[self].keys
    >
    > Is outputing:
    >
    > Hash
    > [[{:strip_comments=>false}], [{:strip_comments=>false}]]
    >
    > How can two identical keys be in a hash?
    >
    > T.


    hi trans-

    afaik multiton.rb is mine

    http://codeforpeople.com/lib/ruby/multiton/multiton-1.0.2/lib/multiton.rb

    the logic behind POOLS is that objects are cached this way

    POOLS[ class_of_object ][ args_given_to_new ] = obj

    in otherwords, contructing two objects with the same argument lists will
    contruct only one object. it's the argument lists which are used to determine
    uniqueness. alternatively uniqueness will be determined via the method
    'multiton_id' if you're class has implimented that instance method or the
    object in question responds to it otherwise. so, in your case, you might use
    something like

    hash.to_a.sort.hash

    or something unique like that. eg

    class MyClass
    include Multiton

    attr :multiton_id

    def initialize h = {}
    @multiton_id = h.to_a.sort.hash
    super
    end
    end

    kind regards.

    -a
    --
    if you find yourself slandering anybody, first imagine that your mouth is
    filled with excrement. it will break you of the habit quickly enough. - the
    dalai lama
    , Dec 26, 2006
    #12
  13. Trans wrote:
    > William James wrote:
    > > Trans wrote:
    > > > thanks Ara and Jon,
    > > >
    > > > I see what your saying. I was using #== not #eql? in comparing the
    > > > keys. So I see why it's faling now. How do I get aorund this? I'm
    > > > caching object based on therr initialization paramaters, which has to
    > > > be an array. Is there a simpler way or do I have to do something like:
    > > >
    > > > class Parameters < Array
    > > > alias :eql? :==
    > > > end
    > > >
    > > > T.

    > >
    > > How about association lists?

    >
    > Thanks William! That's what I did and worked (desipite inefficency).
    >
    > T.


    Here's a speed comparison for various numbers of keys:

    require 'benchmark'

    $iterations = 40_000

    def rand_sym
    letters = ('a'..'z').to_a
    sym = ""
    8.times{ sym << letters[ rand(letters.size) ] }
    sym.to_sym
    end

    def test_assoc n
    alist = []
    keys = []
    while alist.size < n do
    key = rand_sym
    unless keys.include?( key )
    alist << [ key, true ]
    keys << key
    end
    end
    $iterations.times{
    keys.each{|key| fail if alist.assoc(key)[1] != true }
    }
    end

    def test_hash n
    hash = {}
    keys = []
    while hash.size < n
    key = rand_sym
    unless hash.include?( key )
    hash[key] = true
    keys << key
    end
    end
    $iterations.times{
    keys.each{|key| fail if hash[key] != true }
    }
    end

    Benchmark.bm(8) do |x|
    [1,2,3,7,20].each{ |n|
    x.report("assoc %2d" % n) { test_assoc n }
    x.report("hash %2d" % n) { test_hash n }
    }
    end

    user system total real
    assoc 1 0.150000 0.000000 0.150000 ( 0.171000)
    hash 1 0.130000 0.000000 0.130000 ( 0.140000)
    assoc 2 0.291000 0.000000 0.291000 ( 0.310000)
    hash 2 0.200000 0.000000 0.200000 ( 0.231000)
    assoc 3 0.440000 0.000000 0.440000 ( 0.460000)
    hash 3 0.291000 0.000000 0.291000 ( 0.311000)
    assoc 7 1.172000 0.000000 1.172000 ( 1.252000)
    hash 7 0.590000 0.000000 0.590000 ( 0.640000)
    assoc 20 5.188000 0.000000 5.188000 ( 5.578000)
    hash 20 1.652000 0.000000 1.652000 ( 1.813000)
    William James, Dec 26, 2006
    #13
  14. Trans

    Trans Guest

    William James wrote:
    > Here's a speed comparison for various numbers of keys:
    >
    > require 'benchmark'
    >
    > $iterations = 40_000
    >
    > def rand_sym
    > letters = ('a'..'z').to_a
    > sym = ""
    > 8.times{ sym << letters[ rand(letters.size) ] }
    > sym.to_sym
    > end
    >
    > def test_assoc n
    > alist = []
    > keys = []
    > while alist.size < n do
    > key = rand_sym
    > unless keys.include?( key )
    > alist << [ key, true ]
    > keys << key
    > end
    > end
    > $iterations.times{
    > keys.each{|key| fail if alist.assoc(key)[1] != true }
    > }
    > end
    >
    > def test_hash n
    > hash = {}
    > keys = []
    > while hash.size < n
    > key = rand_sym
    > unless hash.include?( key )
    > hash[key] = true
    > keys << key
    > end
    > end
    > $iterations.times{
    > keys.each{|key| fail if hash[key] != true }
    > }
    > end
    >
    > Benchmark.bm(8) do |x|
    > [1,2,3,7,20].each{ |n|
    > x.report("assoc %2d" % n) { test_assoc n }
    > x.report("hash %2d" % n) { test_hash n }
    > }
    > end
    >
    > user system total real
    > assoc 1 0.150000 0.000000 0.150000 ( 0.171000)
    > hash 1 0.130000 0.000000 0.130000 ( 0.140000)
    > assoc 2 0.291000 0.000000 0.291000 ( 0.310000)
    > hash 2 0.200000 0.000000 0.200000 ( 0.231000)
    > assoc 3 0.440000 0.000000 0.440000 ( 0.460000)
    > hash 3 0.291000 0.000000 0.291000 ( 0.311000)
    > assoc 7 1.172000 0.000000 1.172000 ( 1.252000)
    > hash 7 0.590000 0.000000 0.590000 ( 0.640000)
    > assoc 20 5.188000 0.000000 5.188000 ( 5.578000)
    > hash 20 1.652000 0.000000 1.652000 ( 1.813000)


    Nice. Doesn't matter a whole lot a few keys but there is a clear slow
    down.

    I came up with another possibility however. I won;t work for all cases,
    but using Marshal.dup on the args instead of converting to assoc gives
    the proper result too. Wonder how that would benchmark?

    T.
    Trans, Dec 26, 2006
    #14
  15. Trans

    Trans Guest

    wrote:
    > On Wed, 27 Dec 2006, Trans wrote:
    >
    > >> From Facets' multiton.rb (which is primarily Floran Franks' work), I'm

    > > getting somthing that doesn't make any sense:
    > >
    > > POOLS[self] ||= {}
    > > p POOLS[self].class
    > > p POOLS[self].keys
    > >
    > > Is outputing:
    > >
    > > Hash
    > > [[{:strip_comments=>false}], [{:strip_comments=>false}]]
    > >
    > > How can two identical keys be in a hash?
    > >
    > > T.

    >
    > hi trans-
    >
    > afaik multiton.rb is mine
    >
    > http://codeforpeople.com/lib/ruby/multiton/multiton-1.0.2/lib/multiton.rb


    Somehow I got Florian Franks name attached to that. Well, unless
    Florian's got something to say about it, I'll reattribute to you. Sorry
    about that!

    T.
    Trans, Dec 26, 2006
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Neroku
    Replies:
    12
    Views:
    544
    Oliver Wong
    Feb 12, 2007
  2. rp
    Replies:
    1
    Views:
    491
    red floyd
    Nov 10, 2011
  3. Xeno Campanoli
    Replies:
    16
    Views:
    251
    Martin DeMello
    Aug 25, 2005
  4. Malik Yousef

    Sort Hash o Hash accordint to two keys

    Malik Yousef, May 6, 2004, in forum: Perl Misc
    Replies:
    9
    Views:
    185
    Uri Guttman
    May 7, 2004
  5. Malik Yousef

    Sort Hash o Hash accordint to two keys

    Malik Yousef, May 6, 2004, in forum: Perl Misc
    Replies:
    0
    Views:
    97
    Malik Yousef
    May 6, 2004
Loading...

Share This Page