[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Cascading <=> comparisons

Garance A Drosehn

7/16/2005 2:09:00 AM

Let's say I have a hash with some values in it, and I want to
print out that hash in some sort order. I can:

korder = vals.keys.sort { |ka, kb|
vals[kb] <=> vals[ka]
}
korder.each { |key| printf " %3d - %s\n", vals[key], key }

That's pretty easy, but what if I want to cascade comparisons
in that sort, such that if the data-values are equal, then I want
the sort order to be based on some other comparison? It
seems to me that I have to:

korder = vals.keys.sort { |ka, kb|
sres = vals[kb] <=> vals[ka]
sres = ka <=> kb if sres == 0
sres
}
korder.each { |key| printf " %3d - %s\n", vals[key], key }

In perl, I can just casade the '<=>' comparisons, because it
will treat the zero value as "false". Thus, I could get away
with a one-liner somewhat similar to:

vals[kb] <=> vals[ka] or a <=> kb

but that doesn't work in ruby. Is there some other way I could
collapse multiple <=> comparisons into a single statement?
I do not mind the multi-statement form (now that I've convinced
myself that I can't cascade them the way I could in perl), but I'm
just wondering if there's a better way to do it.

--
Garance Alistair Drosehn = drosihn@gmail.com
Senior Systems Programmer or gad@FreeBSD.org
Rensselaer Polytechnic Institute; Troy, NY; USA


8 Answers

dblack

7/16/2005 2:53:00 AM

0

Eric Mahurin

7/16/2005 2:59:00 AM

0



--- Garance A Drosehn <drosihn@gmail.com> wrote:

> Let's say I have a hash with some values in it, and I want to
> print out that hash in some sort order. I can:
>
> korder = vals.keys.sort { |ka, kb|
> vals[kb] <=> vals[ka]
> }
> korder.each { |key| printf " %3d - %s\n", vals[key], key }
>
> That's pretty easy, but what if I want to cascade comparisons
> in that sort, such that if the data-values are equal, then I
> want
> the sort order to be based on some other comparison? It
> seems to me that I have to:
>
> korder = vals.keys.sort { |ka, kb|
> sres = vals[kb] <=> vals[ka]
> sres = ka <=> kb if sres == 0
> sres
> }
> korder.each { |key| printf " %3d - %s\n", vals[key], key }
>
> In perl, I can just casade the '<=>' comparisons, because it
> will treat the zero value as "false". Thus, I could get away
> with a one-liner somewhat similar to:
>
> vals[kb] <=> vals[ka] or a <=> kb
>
> but that doesn't work in ruby. Is there some other way I
> could
> collapse multiple <=> comparisons into a single statement?
> I do not mind the multi-statement form (now that I've
> convinced
> myself that I can't cascade them the way I could in perl),
> but I'm
> just wondering if there's a better way to do it.


(vals[kb] <=> vals[ka]).nonzero? or a <=> kb




____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yaho...



nobu.nokada

7/16/2005 3:02:00 AM

0

Hi,

At Sat, 16 Jul 2005 11:08:57 +0900,
Garance A Drosehn wrote in [ruby-talk:148321]:
> In perl, I can just casade the '<=>' comparisons, because it
> will treat the zero value as "false". Thus, I could get away
> with a one-liner somewhat similar to:
>
> vals[kb] <=> vals[ka] or a <=> kb

(vals[kb] <=> vals[ka]).nonzero? or (ka <=> kb)

--
Nobu Nakada


Ara.T.Howard

7/16/2005 3:34:00 AM

0

Garance A Drosehn

7/16/2005 6:08:00 AM

0

On 7/15/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:
> On Sat, 16 Jul 2005, Garance A Drosehn wrote:
>
> > That's pretty easy, but what if I want to cascade comparisons
> > in that sort, such that if the data-values are equal, then I want
> > the sort order to be based on some other comparison? ...
>
> [vals[kb], ka] <=> [vals[ka], kb]
>
> note that the array approach scales for any number of things,
> not only two. you can always do
>
> [a,b,c] <=> [x,y,z]
>
> even if a, b, and c are different types. so long as you can
>
> a <=> x
> b <=> y
> c <=> z

Ooo. Very nice. Thanks!

The suggestions to look at sort_by (from Enumerable) and
the idea of: (vals[kb] <=> vals[ka]).nonzero? or (ka <=> kb)
were also very useful. Thanks to all.

--
Garance Alistair Drosehn = drosihn@gmail.com
Senior Systems Programmer or gad@FreeBSD.org
Rensselaer Polytechnic Institute; Troy, NY; USA


Robert Klemme

7/16/2005 7:39:00 AM

0

Garance A Drosehn <drosihn@gmail.com> wrote:
> On 7/15/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:
>> On Sat, 16 Jul 2005, Garance A Drosehn wrote:
>>
>>> That's pretty easy, but what if I want to cascade comparisons
>>> in that sort, such that if the data-values are equal, then I want
>>> the sort order to be based on some other comparison? ...
>>
>> [vals[kb], ka] <=> [vals[ka], kb]
>>
>> note that the array approach scales for any number of things,
>> not only two. you can always do
>>
>> [a,b,c] <=> [x,y,z]
>>
>> even if a, b, and c are different types. so long as you can
>>
>> a <=> x
>> b <=> y
>> c <=> z
>
> Ooo. Very nice. Thanks!
>
> The suggestions to look at sort_by (from Enumerable) and
> the idea of: (vals[kb] <=> vals[ka]).nonzero? or (ka <=> kb)
> were also very useful. Thanks to all.

Noone explicitely commented on the use of hash.keys.sort: this is quite
inefficient. Instead doing the sort on the hash directly is much more
efficient:

>> hash = { "a" => 1, "b" => 4, "c" => 3, "d" => 1 }
=> {"a"=>1, "b"=>4, "c"=>3, "d"=>1}
>> hash.sort {|a,b| a.reverse <=> b.reverse}
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

This technique can be applied to sort_by, too:

>> hash.sort_by {|a| a.reverse}
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

But this shows a strange anomaly - this seems like a bug in 1.8.2.

>> hash.sort_by {|a| a.reverse!}
=> [[1, "a"], [1, "d"], [3, "c"], [4, "b"]]

You can also directly chain printing:

>> hash.sort_by {|a| a.reverse}.each {|k,v| printf "%3d %s\n", v, k}
1 a
1 d
3 c
4 b
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

Kind regards

robert

dblack

7/16/2005 12:17:00 PM

0

Garance A Drosehn

7/16/2005 11:38:00 PM

0

On 7/16/05, Robert Klemme <bob.news@gmx.net> wrote:
> Garance A Drosehn <drosihn@gmail.com> wrote:
>
> Noone explicitely commented on the use of hash.keys.sort:
> this is quite inefficient. Instead doing the sort on the hash
> directly is much more efficient:

I can see how this would be faster wrt execution, but if I use
hashobj.sort then I end up with an array of arrays which have
both the key-names and their values. Memory-wise, isn't that
going to be significantly larger than an array which only has
the key-names? When I wrote this, I mainly thought about the
memory usage, and I just assumed that the performance would
be "about-the-same" either way...

I'll check over my actual script wrt this idea. It's probably true
that I would rather optimize the speed than the memory usage,
in which case I might take advantage of this. I'll certainly use
it in other scripts I write! Thanks.

(note that in the actual script, the values are not simple integers,
but objects which in turn have a lot of instance-variables...)

--
Garance Alistair Drosehn = drosihn@gmail.com
Senior Systems Programmer or gad@FreeBSD.org
Rensselaer Polytechnic Institute; Troy, NY; USA