Sorting a Hash

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”

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,
}

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)

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]]

Regards,

Brian.

···

On Tue, Jul 01, 2003 at 07:07:55PM +0900, Osuka wrote:

Brian Candler B.Candler@pobox.com wrote in message news:20030701112013.A20127@linnet.org

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.

···

On Tue, Jul 01, 2003 at 07:07:55PM +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.

···

On Wed, Jul 02, 2003 at 01:50:30AM +0900, Osuka wrote:

“Osuka” oadartse@yahoo.com schrieb im Newsbeitrag
news:9411fd36.0307010845.2eb1149d@posting.google.com

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!!

No, you don’t “assign the returned value to the hash”, because Hashes are
always unsorted. The sort method allows you to process them in order
but you can’t change the internal order in the hash. Of course you can
store the nested array you get from sort. But this is not a Hash any
more.

Cheers

robert

Brian Candler B.Candler@pobox.com wrote in message news:20030701211409.GA66745@uk.tiscali.com

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)

···

On Wed, Jul 02, 2003 at 01:50:30AM +0900, Osuka wrote:

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.

···

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)