Cascading <=> comparisons

G

Garance A Drosehn

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

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

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

but that doesn't work in ruby. Is there some other way I could
collapse multiple <=3D> 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.

--=20
Garance Alistair Drosehn =3D (e-mail address removed)
Senior Systems Programmer or (e-mail address removed)
Rensselaer Polytechnic Institute; Troy, NY; USA
 
D

David A. Black

Hi --

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 }

You can use sort_by and put your sort keys in an array. For example,
this sorts a hash by value and then by key:

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


David
 
E

Eric Mahurin

--- Garance A Drosehn said:
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:
=20
korder =3D vals.keys.sort { |ka, kb|
vals[kb] <=3D> vals[ka]
}
korder.each { |key| printf " %3d - %s\n", vals[key], key }
=20
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:
=20
korder =3D vals.keys.sort { |ka, kb|
sres =3D vals[kb] <=3D> vals[ka]
sres =3D ka <=3D> kb if sres =3D=3D 0
sres
}
korder.each { |key| printf " %3d - %s\n", vals[key], key }
=20
In perl, I can just casade the '<=3D>' comparisons, because it
will treat the zero value as "false". Thus, I could get away
with a one-liner somewhat similar to:
=20
vals[kb] <=3D> vals[ka] or a <=3D> kb
=20
but that doesn't work in ruby. Is there some other way I
could
collapse multiple <=3D> 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] <=3D> vals[ka]).nonzero? or a <=3D> kb



=09
____________________________________________________
Start your day with Yahoo! - make it your home page=20
http://www.yahoo.com/r/hs=20
=20
 
N

nobu.nokada

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)
 
A

Ara.T.Howard

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

[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

hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
G

Garance A Drosehn

On Sat, 16 Jul 2005, Garance A Drosehn wrote:
=20
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? ...
=20
[vals[kb], ka] <=3D> [vals[ka], kb]
=20
note that the array approach scales for any number of things,
not only two. you can always do
=20
[a,b,c] <=3D> [x,y,z]
=20
even if a, b, and c are different types. so long as you can
=20
a <=3D> x
b <=3D> y
c <=3D> z

Ooo. Very nice. Thanks!

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

--=20
Garance Alistair Drosehn =3D (e-mail address removed)
Senior Systems Programmer or (e-mail address removed)
Rensselaer Polytechnic Institute; Troy, NY; USA
 
R

Robert Klemme

Garance A Drosehn said:
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:
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

This technique can be applied to sort_by, too:
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

But this shows a strange anomaly - this seems like a bug in 1.8.2.
=> [[1, "a"], [1, "d"], [3, "c"], [4, "b"]]

You can also directly chain printing:
1 a
1 d
3 c
4 b
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

Kind regards

robert
 
D

David A. Black

Hi --

This technique can be applied to sort_by, too:
=> [["a", 1], ["d", 1], ["c", 3], ["b", 4]]

But this shows a strange anomaly - this seems like a bug in 1.8.2.
=> [[1, "a"], [1, "d"], [3, "c"], [4, "b"]]

I'll probably figure this out as soon as I hit 'send'... but what's
the anomaly here?


David
 
G

Garance A Drosehn

=20
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...)

--=20
Garance Alistair Drosehn =3D (e-mail address removed)
Senior Systems Programmer or (e-mail address removed)
Rensselaer Polytechnic Institute; Troy, NY; USA
 
R

Robert Klemme

Garance A Drosehn said:
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...)

That doesn't make a difference as they are not copied. Mem overhead is just
the temp arrays.

Kind regards

robert
 
R

Robert Klemme

David A. Black said:
Hi --

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

I'll probably figure this out as soon as I hit 'send'... but what's
the anomaly here?

And, did you figure in the meantime? It's the different result - IMHO
anything that sort_by does should have no such effect on the result.

Cheers

robert
 
M

Michael Campbell

Robert Klemme said:
David A. Black said:
Hi --

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

I'll probably figure this out as soon as I hit 'send'... but what's
the anomaly here?

And, did you figure in the meantime? It's the different result -
IMHO anything that sort_by does should have no such effect on the
result.

Even if the operation is a "bang" one?
 
R

Robert Klemme

Michael Campbell said:
Robert Klemme said:
David A. Black said:
Hi --

On Sat, 16 Jul 2005, Robert Klemme wrote:

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

I'll probably figure this out as soon as I hit 'send'... but what's
the anomaly here?

And, did you figure in the meantime? It's the different result -
IMHO anything that sort_by does should have no such effect on the
result.

Even if the operation is a "bang" one?

IMO yes.

robert
 
M

Michael Campbell

Robert Klemme said:
Michael Campbell said:
Robert Klemme said:
Hi --
On Sat, 16 Jul 2005, Robert Klemme wrote:

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"]]
I'll probably figure this out as soon as I hit 'send'... but what's
the anomaly here?
And, did you figure in the meantime? It's the different result -
IMHO anything that sort_by does should have no such effect on the
result.
Even if the operation is a "bang" one?

IMO yes.

Wow. How do you tell ruby to not do what it normally would? How do
reconcile that sort_by {|a| <x>} is different than sort_by {|a| <x!> }
for any given <x>? What about OTHER side effects of <X> (like, a
database call or something)?
 
R

Robert Klemme

Michael Campbell said:
Robert Klemme said:
Michael Campbell said:
Hi --
On Sat, 16 Jul 2005, Robert Klemme wrote:

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"]]
I'll probably figure this out as soon as I hit 'send'... but
what's the anomaly here?
And, did you figure in the meantime? It's the different result -
IMHO anything that sort_by does should have no such effect on the
result.
Even if the operation is a "bang" one?

IMO yes.

Wow. How do you tell ruby to not do what it normally would? How do
reconcile that sort_by {|a| <x>} is different than sort_by {|a| <x!> }
for any given <x>? What about OTHER side effects of <X> (like, a
database call or something)?

Note that in this special example we are sorting a hash and x is an array of
two elements (key and value) - not necessarily something I'd expect to be
part of the internal storage structure of a Hash. I didn't find
documentation that the x in this case is actually part of the internal
structure of the Hash and must not me modified. There are several
solutions:

i) Make a copy (this is inefficient if done for each pair)

ii) Make the two element arrays immutable (this might be difficult because
normally frozen instances cannot be heated again)

iii) Document the current behavior.

Kind regards

robert
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,176
Messages
2,570,947
Members
47,498
Latest member
log5Sshell/alfa5

Latest Threads

Top