Ordered hash hack for < ruby 1.9?

Discussion in 'Ruby' started by Ben Johnson, Oct 1, 2008.

  1. Ben Johnson

    Ben Johnson Guest

    I am having an issue testing my code because hashes don't have a
    consistent order when you iterate over them. For example, one of the
    methods that I am trying to test iterates over a hash and creates a
    string. That string is in a different order every time, and my tests
    keep failing.

    What would be perfect is if I could modify how the Hash class works so
    it preserves the insert order, but only when I am testing. So I could
    include this file in my tests.

    Is there anything out there that does this? Thanks for your help.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #1
    1. Advertising

  2. On Wed, Oct 1, 2008 at 3:00 PM, Ben Johnson <> wrote:
    > I am having an issue testing my code because hashes don't have a
    > consistent order when you iterate over them. For example, one of the
    > methods that I am trying to test iterates over a hash and creates a
    > string. That string is in a different order every time, and my tests
    > keep failing.
    >
    > What would be perfect is if I could modify how the Hash class works so
    > it preserves the insert order, but only when I am testing. So I could
    > include this file in my tests.
    >
    > Is there anything out there that does this? Thanks for your help.


    Hash's == method compares if two hashes are equal (
    http://www.ruby-doc.org/core/classes/Hash.html#M002875 )


    Bye


    --
    Luis Parravicini
    http://ktulu.com.ar/blog/
    Luis Parravicini, Oct 1, 2008
    #2
    1. Advertising

  3. [Note: parts of this message were removed to make it a legal post.]

    Why not iterate over myhash.keys.sort instead of just myhash.keys?

    --wpd


    On Wed, Oct 1, 2008 at 2:00 PM, Ben Johnson <>wrote:

    > I am having an issue testing my code because hashes don't have a
    > consistent order when you iterate over them. For example, one of the
    > methods that I am trying to test iterates over a hash and creates a
    > string. That string is in a different order every time, and my tests
    > keep failing.
    >
    > What would be perfect is if I could modify how the Hash class works so
    > it preserves the insert order, but only when I am testing. So I could
    > include this file in my tests.
    >
    > Is there anything out there that does this? Thanks for your help.
    > --
    > Posted via http://www.ruby-forum.com/.
    >
    >
    Patrick Doyle, Oct 1, 2008
    #3
  4. Ben Johnson

    Ben Johnson Guest

    Luis Parravicini wrote:
    > On Wed, Oct 1, 2008 at 3:00 PM, Ben Johnson <>
    > wrote:
    >> Is there anything out there that does this? Thanks for your help.

    > Hash's == method compares if two hashes are equal (
    > http://www.ruby-doc.org/core/classes/Hash.html#M002875 )
    >
    >
    > Bye


    I realize that. My class is creating a hash of complex objects. For me
    to create the hash by hand would take a long time and be a huge pain in
    the ass. Plus this method does other things with the hash, and
    ultimately returns a string. I want to test this method and it's
    impossible since ruby iterates over a hash in a random order.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #4
  5. Ben Johnson

    ara.t.howard Guest

    On Oct 1, 2008, at 12:00 PM, Ben Johnson wrote:

    > What would be perfect is if I could modify how the Hash class works so
    > it preserves the insert order, but only when I am testing. So I could
    > include this file in my tests.
    >
    > Is there anything out there that does this? Thanks for your help.
    > --



    gem install orderedhash


    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Oct 1, 2008
    #5
  6. Ben Johnson

    Ben Johnson Guest

    Patrick Doyle wrote:
    > Why not iterate over myhash.keys.sort instead of just myhash.keys?
    >
    > --wpd


    Because for performance it's bad. I don't care if performance is bad in
    my tests. Which is why it would be nice to alter how hashes work ONLY in
    my test environment. But sorting by keys isn't smart either.

    >> a = {:a => 1, :b => 2}

    => {:a=>1, :b=>2}
    >> a.keys.sort

    NoMethodError: undefined method `<=>' for :a:Symbol
    from (irb):16:in `sort'
    from (irb):16
    from :0

    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #6
  7. Luis Parravicini, Oct 1, 2008
    #7
  8. Ben Johnson

    Lex Williams Guest

    Ben Johnson wrote:
    > Patrick Doyle wrote:
    >> Why not iterate over myhash.keys.sort instead of just myhash.keys?
    >>
    >> --wpd

    >
    > Because for performance it's bad. I don't care if performance is bad in
    > my tests. Which is why it would be nice to alter how hashes work ONLY in
    > my test environment. But sorting by keys isn't smart either.
    >
    >>> a = {:a => 1, :b => 2}

    > => {:a=>1, :b=>2}
    >>> a.keys.sort

    > NoMethodError: undefined method `<=>' for :a:Symbol
    > from (irb):16:in `sort'
    > from (irb):16
    > from :0


    Here's a hack for altering the original Hash class :

    class Hash
    alias :eek:ld_equals :[]=
    attr_reader :eek:rdered_values

    def []=(key,value)
    @ordered_values ||= []
    @ordered_values << key
    old_equals(key,value)
    end


    end

    hsh = {}
    hsh["a"]="b"
    hsh["b"]="c"
    hsh.ordered_values.each do |key|
    puts key
    end

    ###
    outputs a,b
    --
    Posted via http://www.ruby-forum.com/.
    Lex Williams, Oct 1, 2008
    #8
  9. Ben Johnson

    Lex Williams Guest

    That should have been called ordered_keys instead of ordered_values ...
    sorry , speed coding does this to me. But,in rest,the code works .
    --
    Posted via http://www.ruby-forum.com/.
    Lex Williams, Oct 1, 2008
    #9
  10. Ben Johnson

    Ben Johnson Guest

    Lex Williams wrote:
    > That should have been called ordered_keys instead of ordered_values ...
    > sorry , speed coding does this to me. But,in rest,the code works .


    Awesome, this is exactly what I need, the only problem is that it
    doesn't work when you do:

    hsh = {"a" => "b"}

    I'm trying to get this to work but having no luck.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #10
  11. Ben Johnson

    Ben Johnson Guest

    Lex Williams wrote:
    > That should have been called ordered_keys instead of ordered_values ...
    > sorry , speed coding does this to me. But,in rest,the code works .


    This seemed to do the trick for me:

    class Hash
    def each(&block)
    sorted_keys = keys.sort { |a, b| a.to_s <=> b.to_s }
    sorted_keys.each do |key|
    yield key, self[key]
    end
    self
    end
    end

    Thanks for your help.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #11
  12. Ben Johnson

    Lex Williams Guest

    Lex Williams, Oct 1, 2008
    #12
  13. Ben Johnson

    Trans Guest

    On Oct 1, 2:12=A0pm, Ben Johnson <> wrote:
    > Luis Parravicini wrote:
    > > On Wed, Oct 1, 2008 at 3:00 PM, Ben Johnson <>
    > > wrote:
    > >> Is there anything out there that does this? Thanks for your help.

    > > Hash's =3D=3D method compares if two hashes are equal (
    > >http://www.ruby-doc.org/core/classes/Hash.html#M002875)

    >
    > > Bye

    >
    > I realize that. My class is creating a hash of complex objects. For me
    > to create the hash by hand would take a long time and be a huge pain in
    > the ass. Plus this method does other things with the hash, and
    > ultimately returns a string. I want to test this method and it's
    > impossible since ruby iterates over a hash in a random order.


    Since a hash is an unordered collection, one should not test it
    according to any order. Instead test to see if a key is present and it
    has certain values.

    That you want it ordered should raise a red flag for you.

    T.
    Trans, Oct 1, 2008
    #13
  14. Ben Johnson

    Lex Williams Guest

    Lex Williams, Oct 1, 2008
    #14
  15. Ben Johnson

    Ben Johnson Guest

    Lex Williams wrote:
    > this sorts them alphabetically , not by insertion order.


    I didn't need them by insertion order, just to be consistent. I
    apologize if I made that unclear.

    Also, I am not testing a hash, I am testing a method that returns a
    string. That string is built by iterating over a hash.

    Thanks.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 1, 2008
    #15
  16. Ben Johnson wrote:
    > Lex Williams wrote:
    >> this sorts them alphabetically , not by insertion order.

    >
    > I didn't need them by insertion order, just to be consistent. I
    > apologize if I made that unclear.
    >
    > Also, I am not testing a hash, I am testing a method that returns a
    > string. That string is built by iterating over a hash.
    >
    > Thanks.


    This code fills a hash and then iterates the hash to build a string.

    h={}
    ("a".."z").each{|i| h = i}
    10.times do
    res = h.inject(""){|str, pair| str<<pair[1]}
    puts res
    end

    The string is always "vkwlaxmbynczodpeqfrgshtiuj" on my machine. There
    is no visible logic, but it is predictable and testable. However, the
    test may fail when you install the next ruby version, or perhaps when
    run on a another platform.
    I don't know why your code behaves differently, but if the insertion
    order in the hash is not predictable (threads ?), then an ordered hash
    won't help.

    regards,

    Siep
    --
    Posted via http://www.ruby-forum.com/.
    Siep Korteling, Oct 1, 2008
    #16
  17. Ben Johnson

    Ken Bloom Guest

    On Wed, 01 Oct 2008 14:05:33 -0500, Lex Williams wrote:

    > I'm guessing ruby doesn't have a LinkedHashMap kind of collection ,
    > right ? :)


    Use a Dictionary from the factets gem if that's what you want.

    --Ken

    --
    Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory.
    Department of Computer Science. Illinois Institute of Technology.
    http://www.iit.edu/~kbloom1/
    Ken Bloom, Oct 2, 2008
    #17
  18. Lex Williams wrote:
    > Ben Johnson wrote:
    >> Patrick Doyle wrote:
    >>> Why not iterate over myhash.keys.sort instead of just myhash.keys?
    >>>
    >>> --wpd

    >>
    >> Because for performance it's bad. I don't care if performance is bad in
    >> my tests. Which is why it would be nice to alter how hashes work ONLY in
    >> my test environment. But sorting by keys isn't smart either.
    >>
    >>>> a = {:a => 1, :b => 2}

    >> => {:a=>1, :b=>2}
    >>>> a.keys.sort

    >> NoMethodError: undefined method `<=>' for :a:Symbol
    >> from (irb):16:in `sort'
    >> from (irb):16
    >> from :0

    >
    > Here's a hack for altering the original Hash class :
    >
    > class Hash
    > alias :eek:ld_equals :[]=
    > attr_reader :eek:rdered_values
    >
    > def []=(key,value)
    > @ordered_values ||= []
    > @ordered_values << key
    > old_equals(key,value)
    > end
    >
    >
    > end
    >
    > hsh = {}
    > hsh["a"]="b"
    > hsh["b"]="c"
    > hsh.ordered_values.each do |key|
    > puts key
    > end


    hsh.delete("a")
    hsh.ordered_values.each do |key|
    puts key
    end

    Does not output what you want. :)

    There are a number of recommended packages already that should do this,
    use one of them.. Not to mention this monkeypatch is going to slow down
    your whole program, when you likely just need it in one or two spots.

    If you do have to code it yourself, consider using the 'delegate'
    library (in stdlib) to wrap your specific, non-standard logic into its
    own class, where it can be managed separately from standard hashes.

    Yours in preventing senseless monkeypatching,

    -Erik
    --
    Posted via http://www.ruby-forum.com/.
    Erik Hollensbe, Oct 3, 2008
    #18
  19. Ben Johnson

    Ben Johnson Guest

    Erik Hollensbe wrote:
    > There are a number of recommended packages already that should do this,
    > use one of them.. Not to mention this monkeypatch is going to slow down
    > your whole program, when you likely just need it in one or two spots.
    >
    > If you do have to code it yourself, consider using the 'delegate'
    > library (in stdlib) to wrap your specific, non-standard logic into its
    > own class, where it can be managed separately from standard hashes.
    >
    > Yours in preventing senseless monkeypatching,
    >
    > -Erik


    Thanks Erik, I agree 100%. I ONLY applied this when testing, where
    performance really isn't an issue. I just needed hashes to iterate in a
    consistent order. What was unique about my situation is that the hash
    order didn't matter to the functional purpose of the method. It mattered
    only when I needed to do assertions in my tests.

    Honestly, if order if meaningful you should use an array. I think any of
    the above gems are not the best choice when it comes to performance.
    --
    Posted via http://www.ruby-forum.com/.
    Ben Johnson, Oct 3, 2008
    #19
  20. Siep Korteling wrote:
    > The string is always "vkwlaxmbynczodpeqfrgshtiuj" on my machine. There
    > is no visible logic, but it is predictable and testable. However, the
    > test may fail when you install the next ruby version, or perhaps when
    > run on a another platform.
    > I don't know why your code behaves differently, but if the insertion
    > order in the hash is not predictable (threads ?), then an ordered hash
    > won't help.


    Hash ordering is generally deterministic on read until another write
    occurs. Then it all depends on the algorithm used to hash the key, if
    the bucket size was changed and rekeying occured, a number of things.
    The RHG I believe goes into how hashing algorithms work in ruby, but a
    good example I can definitely cite is perl 5.6 (and prior)'s hashing
    algorithm, which if seeded properly could cause it to resize the buckets
    and re-key so often it could bring system load to DoS levels.... Yes,
    that's one program, with one hash, with a lot of pairs inserted one at a
    time, specifically ordered to cause the algorithm to internally resize
    and rekey on each insert (you know, for performance). There's a bugtraq
    posting from ... 2002? 2003? that goes into the specific method if
    you're interested (and yes, it was fixed in 5.8).

    Anyways, that's not intended as a dig on perl, but basically it's not
    something to count on, ever.

    -Erik
    --
    Posted via http://www.ruby-forum.com/.
    Erik Hollensbe, Oct 3, 2008
    #20
    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. rp
    Replies:
    1
    Views:
    490
    red floyd
    Nov 10, 2011
  2. robertj

    ordered/sorted hash

    robertj, Dec 8, 2005, in forum: Ruby
    Replies:
    19
    Views:
    193
    William James
    Dec 11, 2005
  3. Hal Fulton

    A use case for an ordered hash

    Hal Fulton, Aug 13, 2006, in forum: Ruby
    Replies:
    89
    Views:
    632
    Haoqi Haoqi
    May 5, 2007
  4. Devi Web Development

    Ordered Hash Usefulness

    Devi Web Development, Nov 13, 2007, in forum: Ruby
    Replies:
    18
    Views:
    246
    Ralph Siegler
    Nov 26, 2007
  5. DL

    Ordered list inside ordered list

    DL, Nov 9, 2009, in forum: Javascript
    Replies:
    6
    Views:
    304
    Dr J R Stockton
    Nov 21, 2009
Loading...

Share This Page