Sorting a Hash

Discussion in 'Ruby' started by Osuka, Jul 1, 2003.

  1. Osuka

    Osuka Guest

    I'm Having some trouble sorting a hash!, the hash contents are like
    this:

    1 is Ayumi Hamasaki
    2 is Zone
    1 is Two-Mix
    2 is Shazna
    1 is L'arc~en~ciel

    Problem is: I want to ordered it in a descending order but using
    Hash.invert I lose data 'cos there can't be several keys that are the
    same, so any ideas of how to do this

    this is the code the comments are there 'cos they del some data
    ---------------CODE------------------------
    jdir="E:/emusic/jrock"
    Dir.chdir(jdir)
    jlist=Array.new
    jlist=Dir.entries(jdir)
    jlist.sort!
    $serie=Array.new
    $jcount=Hash.new()
    jlist.each do |entry|
    $serie=entry.sub(/ - +[a-zA-Z0-9\-.!=&+~\(\) ']*/,'')
    if($jcount.has_key?($serie))
    $jcount[$serie]=$jcount[$serie]+1
    else
    $jcount.store($serie,1)
    end
    end
    #$jcount=$jcount.invert
    #$jcount=$jcount.sort
    #$jcount.reverse!
    $jcount.each {|key, value| print value, " is ", key, "\n" }
    ---------------CODE------------------------

    comments on the regexp are also welcomed
    "artist - name(~=live'now-now'~[what])-.mp3" to "artist"
    Osuka, Jul 1, 2003
    #1
    1. Advertising

  2. Osuka

    Osuka Guest

    Brian Candler <> wrote in message news:<>...
    > On Tue, Jul 01, 2003 at 07:07:55PM +0900, Osuka wrote:
    > > I'm Having some trouble sorting a hash!, the hash contents are like
    > > this:
    > >
    > > 1 is Ayumi Hamasaki
    > > 2 is Zone
    > > 1 is Two-Mix
    > > 2 is Shazna
    > > 1 is L'arc~en~ciel

    >
    > I don't understand. Firstly, do you mean that your hash is like this:
    >
    > myhash = {
    > 'Ayumi Hamasaki' => 1,
    > 'Zone' => 2,
    > 'Two-Mix' => 1,
    > 'Shazna' => 2,
    > 'L\'Arc~en~ciel' => 1,
    > }
    >

    Sorry about that!! but you got the right idea!!

    > Secondly, how do you want to sort it? If you just do
    >
    > myhash.sort
    >
    > then you will get it sorted alphabetically by the key, i.e.
    >
    > => [["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1], ["Shazna", 2],
    > ["Two-Mix", 1], ["Zone", 2]]
    >
    > If you want to sort it by the numeric value, then you can pass in an
    > explicit block which shows how to compare the values:
    >
    > myhash.sort {|x,y| x[1] <=> y[1]}
    >
    > => [["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1], ["Two-Mix", 1],
    > ["Shazna", 2], ["Zone", 2]]
    >
    > (you can see all the 1's come before the 2's)


    Arrg I did tried this and the to_a approach but i forgot something way
    too simple! if myhash.sort {|x,y| x[1] <=> y[1]} does return the
    sorted array but doesn't actually affects the given array so I have to
    assign the returned value to the hash I'm using, how come I miss it!!!
    all the problem was here!!

    Yes I want it sorted by descending values not alphabetical
    [["Shazna", 2],["Zone", 2],["Ayumi Hamasaki", 1], ["L'Arc~en~ciel",
    1], ["Two-Mix", 1]]

    >
    > Essentially the thing to remember is: when you sort a hash, it first gets
    > converted to an array, where each element is a two-element array of
    > [key,value] pairs.
    >
    > You can do this conversion yourself explicitly:
    >
    > myhash.to_a
    >
    > > Problem is: I want to ordered it in a descending order but using
    > > Hash.invert I lose data 'cos there can't be several keys that are the
    > > same, so any ideas of how to do this

    >
    > By descending order of what?
    >
    > Hash.invert doesn't reverse the order of elements, it swaps the keys and the
    > values!! If you want a reverse alphabetical sort, try:
    >
    > myhash.sort.reverse
    >
    > => [["Zone", 2], ["Two-Mix", 1], ["Shazna", 2], ["L'Arc~en~ciel", 1],
    > ["Ayumi Hamasaki", 1]]
    >


    yes values become keys and keys values, that's why it was "working"
    but eliminating some new keys ie duplicated ones. doing a reverse then
    converting to an array meaned that I could easily get it sorted as I
    wanted but I would lose elements because of key duplicates, which its
    my fault for not considering that a lot of the values are identical.
    > Regards,
    >
    > Brian.


    well thanks!! now it works and sorry for a not so clear post.
    Osuka, Jul 1, 2003
    #2
    1. Advertising

  3. On Wed, Jul 02, 2003 at 01:50:30AM +0900, Osuka wrote:
    > > If you want to sort it by the numeric value, then you can pass in an
    > > explicit block which shows how to compare the values:
    > >
    > > myhash.sort {|x,y| x[1] <=> y[1]}
    > >
    > > => [["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1], ["Two-Mix", 1],
    > > ["Shazna", 2], ["Zone", 2]]
    > >
    > > (you can see all the 1's come before the 2's)

    >
    > Arrg I did tried this and the to_a approach but i forgot something way
    > too simple! if myhash.sort {|x,y| x[1] <=> y[1]} does return the
    > sorted array but doesn't actually affects the given array so I have to
    > assign the returned value to the hash I'm using, how come I miss it!!!
    > all the problem was here!!


    Of course, the result of the sort is an array, not a hash. There's no point
    trying to put the elements back into a hash, because a hash is _unordered_
    by definition. Effectively they would end up in a random order again.

    > Yes I want it sorted by descending values not alphabetical
    > [["Shazna", 2],["Zone", 2],["Ayumi Hamasaki", 1], ["L'Arc~en~ciel",
    > 1], ["Two-Mix", 1]]


    Ah OK. Actually quite a nice solution is:

    myhash.sort { |x,y| y[1] <=> x[1] }

    or even better,

    myhash.sort { |x,y| y.reverse <=> x.reverse }

    The 'reverse' swaps x[0]/x[1], so it sorts by the number before sorting by
    the name. Using y...<=>...x gives a sort in reverse order. So this will
    group together all the entries by number, sorted in reverse numeric order,
    and within groups it will sort by reverse alphabetic order.

    If you want reverse numeric and forward alphabetic, you can get a bit more
    imaginative with the contents of the comparison function:

    myhash.sort { |x,y|
    cmp = y[1] <=> x[1]
    if cmp != 0
    cmp
    else
    x[0] <=> y[0]
    end
    }

    => [["Shazna", 2], ["Zone", 2], ["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1],
    ["Two-Mix", 1]]

    Cheers,

    Brian.
    Brian Candler, Jul 1, 2003
    #3
  4. Osuka

    Osuka Guest

    Brian Candler <> wrote in message news:<>...
    > On Wed, Jul 02, 2003 at 01:50:30AM +0900, Osuka wrote:
    > > > If you want to sort it by the numeric value, then you can pass in an
    > > > explicit block which shows how to compare the values:
    > > >
    > > > myhash.sort {|x,y| x[1] <=> y[1]}
    > > >
    > > > => [["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1], ["Two-Mix", 1],
    > > > ["Shazna", 2], ["Zone", 2]]
    > > >
    > > > (you can see all the 1's come before the 2's)

    > >
    > > Arrg I did tried this and the to_a approach but i forgot something way
    > > too simple! if myhash.sort {|x,y| x[1] <=> y[1]} does return the
    > > sorted array but doesn't actually affects the given array so I have to
    > > assign the returned value to the hash I'm using, how come I miss it!!!
    > > all the problem was here!!

    >
    > Of course, the result of the sort is an array, not a hash. There's no point
    > trying to put the elements back into a hash, because a hash is _unordered_
    > by definition. Effectively they would end up in a random order again.
    >
    > > Yes I want it sorted by descending values not alphabetical
    > > [["Shazna", 2],["Zone", 2],["Ayumi Hamasaki", 1], ["L'Arc~en~ciel",
    > > 1], ["Two-Mix", 1]]

    >
    > Ah OK. Actually quite a nice solution is:
    >
    > myhash.sort { |x,y| y[1] <=> x[1] }
    >
    > or even better,
    >
    > myhash.sort { |x,y| y.reverse <=> x.reverse }
    >
    > The 'reverse' swaps x[0]/x[1], so it sorts by the number before sorting by
    > the name. Using y...<=>...x gives a sort in reverse order. So this will
    > group together all the entries by number, sorted in reverse numeric order,
    > and within groups it will sort by reverse alphabetic order.
    >
    > If you want reverse numeric and forward alphabetic, you can get a bit more
    > imaginative with the contents of the comparison function:
    >
    > myhash.sort { |x,y|
    > cmp = y[1] <=> x[1]
    > if cmp != 0
    > cmp
    > else
    > x[0] <=> y[0]
    > end
    > }
    >
    > => [["Shazna", 2], ["Zone", 2], ["Ayumi Hamasaki", 1], ["L'Arc~en~ciel", 1],
    > ["Two-Mix", 1]]
    >
    > Cheers,
    >
    > Brian.


    Yes I mistyped there!! I tried to mean the array returned, 'cos the
    hash its no more after a sort, I should be more careful with those
    words. And thanks a lot Brian I didn't thought of doing an
    alphabetical(as secondary rule) sort after the numerical, I guess that
    was coming even in the example that I gave on what I wanted I did it
    like that.Now it all works and as a little exercise I turned it into a
    class(a bit of overkill but...gotta learn)
    Osuka, Jul 3, 2003
    #4
  5. On Thu, Jul 03, 2003 at 08:24:37AM +0900, Osuka wrote:
    > Now it all works and as a little exercise I turned it into a
    > class(a bit of overkill but...gotta learn)


    I don't think it's overkill at all. In Ruby it often makes sense to make a
    class even for simple cases.

    Your code will probably be clearer because the class will have its own <=>
    compare method, so you can sort those objects in their preferred order
    without having to write that magic compare block each time.

    Cheers,

    Brian.
    Brian Candler, Jul 3, 2003
    #5
    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. Replies:
    2
    Views:
    1,401
    James Kanze
    Jul 6, 2010
  2. rp
    Replies:
    1
    Views:
    491
    red floyd
    Nov 10, 2011
  3. Williams, Chris
    Replies:
    3
    Views:
    96
    Florian Gross
    Dec 13, 2004
  4. Srijayanth Sridhar
    Replies:
    19
    Views:
    594
    David A. Black
    Jul 2, 2008
  5. IanW
    Replies:
    3
    Views:
    121
    Ian Stuart
    Dec 14, 2005
Loading...

Share This Page