Finding ties in sorting?

A

Axel Etzold

Dear all,

I have many arrays like

a=[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]

which I'd like to sort such that I get the hashes with the highest
values of 'a' first. If there are ties, I'd like to sort them (but not
the entire array) such
that the highest values of 'b' come first.
So I can't just sort for 'a'-values first and then for 'b'-values,
as this would destroy the first sort order.

Is there a built-in way of getting the ties in sorting, such as an
Array of the results of the <=> comparisons ?

Thank you!

Best regards,

Axel
 
S

Stefano Crocco

Alle venerd=EC 14 settembre 2007, Axel Etzold ha scritto:
Dear all,

I have many arrays like

a=3D[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]

which I'd like to sort such that I get the hashes with the highest
values of 'a' first. If there are ties, I'd like to sort them (but not
the entire array) such
that the highest values of 'b' come first.
So I can't just sort for 'a'-values first and then for 'b'-values,
as this would destroy the first sort order.

Is there a built-in way of getting the ties in sorting, such as an
Array of the results of the <=3D> comparisons ?

Thank you!

Best regards,

Axel

This should work:

a.sort_by{|i| [i['a'], i['b']]}.reverse

a.sort_by returns the hashes sorted by the values of 'a' and, in case of a=
=20
tie, the values of 'b', both in ascending order. Applying reverse to this=20
array gives you the correct order. The result is the following:

[{"a"=3D>13, "b"=3D>13}, {"a"=3D>10, "b"=3D>7}, {"a"=3D>10, "b"=3D>4}, {"a"=
=3D>10, "b"=3D>3},=20
{"a"=3D>5, "b"=3D>13}, {"a"=3D>5, "b"=3D>3}]

I hope this helps

Stefano
 
D

Dan Zwell

Axel said:
Dear all,

I have many arrays like

a=[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]

which I'd like to sort such that I get the hashes with the highest
values of 'a' first. If there are ties, I'd like to sort them (but not
the entire array) such
that the highest values of 'b' come first.
So I can't just sort for 'a'-values first and then for 'b'-values,
as this would destroy the first sort order.

Is there a built-in way of getting the ties in sorting, such as an
Array of the results of the <=> comparisons ?

Thank you!

Best regards,

Axel

Built in? I don't know about that, but it's pretty easy. I'm sure your
real situation is more complex than the example you gave, but I really
think this is the easiest way (and you can avoid making comparisons
twice by caching the value of hash1['a'] <=> hash2['a'], if you wish):

a=[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]
a.sort do |hash1, hash2|
if hash1['a'] == hash2['a']
hash1['b'] <=> hash2['b']
else
hash1['a'] <=> hash2['a']
end
end

The above is essentially a redefinition of <=> on hashes with elements
"a" and "b". The only thing you need to be sure of is that ordering is
strict--that you never have x < y < z but z < x. That should be no
problem if you only try to break ties.

Hope this helps,
Dan
 
R

Rob Biedenharn

Axel said:
Dear all,
I have many arrays like
a=[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]
which I'd like to sort such that I get the hashes with the highest
values of 'a' first. If there are ties, I'd like to sort them (but
not
the entire array) such
that the highest values of 'b' come first.
So I can't just sort for 'a'-values first and then for 'b'-values,
as this would destroy the first sort order.
Is there a built-in way of getting the ties in sorting, such as an
Array of the results of the <=> comparisons ?
Thank you!
Best regards,
Axel

Built in? I don't know about that, but it's pretty easy. I'm sure
your real situation is more complex than the example you gave, but
I really think this is the easiest way (and you can avoid making
comparisons twice by caching the value of hash1['a'] <=> hash2
['a'], if you wish):

a=[{'a',10,'b',3},{'a',10,'b',4},{'a',5,'b',13},{'a',13,'b',13},
{'a',10,'b',7},{'a',5,'b',3}]
a.sort do |hash1, hash2|
if hash1['a'] == hash2['a']
hash1['b'] <=> hash2['b']
else
hash1['a'] <=> hash2['a']
end
end

The above is essentially a redefinition of <=> on hashes with
elements "a" and "b". The only thing you need to be sure of is that
ordering is strict--that you never have x < y < z but z < x. That
should be no problem if you only try to break ties.

Hope this helps,
Dan

a.sort do |h1,h2|
(h1['a'] <=> h2['a']).nonzero? || h1['b'] <=> h2['b']
end

The Numeric#nonzero? is exactly for this kind of thing. If its
receiver is 0 it returns nil so chaining with || will work. (And you
don't have to compare the 'a' values twice.)

-Rob

Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
D

Dan Zwell

Rob said:
a.sort do |h1,h2|
(h1['a'] <=> h2['a']).nonzero? || h1['b'] <=> h2['b']
end

The Numeric#nonzero? is exactly for this kind of thing. If its receiver
is 0 it returns nil so chaining with || will work. (And you don't have
to compare the 'a' values twice.)

-Rob
Neat, I didn't know about that.

Dan
 

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
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top