[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Sorting decimal index numbers?

Aaron Vegh

3/28/2009 2:19:00 AM

Hi there,
I'm working with a list of items that contain decimal-based index
numbers. I'm sorry I don't know the term for these kinds of numbers, and
I'm having trouble finding out how to sort them properly. To wit:

1.1
1.10
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9

When the index numbers are float or strings, they sort like this. But I
want "1.10" to be the last number. Any idea how to put these guys in the
right order?

Thanks!
Aaron.
--
Posted via http://www.ruby-....

9 Answers

Yossef Mendelssohn

3/28/2009 2:32:00 AM

0

On Mar 27, 9:18=A0pm, Aaron Vegh <aa...@vegh.ca> wrote:
> When the index numbers are float or strings, they sort like this. But I
> want "1.10" to be the last number. Any idea how to put these guys in the
> right order?

$ irb
>> nums =3D ['1.1', '1.2', '1.10', '1.9']
=3D> ["1.1", "1.2", "1.10", "1.9"]
>> nums.sort
=3D> ["1.1", "1.10", "1.2", "1.9"]
>> nums.collect { |n| n.split('.').collect { |i|
i.to_i } }.sort.collect { |n| n.join('.') }
=3D> ["1.1", "1.2", "1.9", "1.10"]

--
-yossef

matt

3/28/2009 2:49:00 AM

0

Aaron Vegh <aaron@vegh.ca> wrote:

> Hi there,
> I'm working with a list of items that contain decimal-based index
> numbers. I'm sorry I don't know the term for these kinds of numbers, and
> I'm having trouble finding out how to sort them properly. To wit:
>
> 1.1
> 1.10
> 1.2
> 1.3
> 1.4
> 1.5
> 1.6
> 1.7
> 1.8
> 1.9
>
> When the index numbers are float or strings, they sort like this. But I
> want "1.10" to be the last number. Any idea how to put these guys in the
> right order?

These are not numbers; they're strings. Or at least, they need to be; if
they weren't, 1.1 and 1.10 would be indistinguishable. So let's assume
they *are* strings. Then what you're asking to do is to compare the two
halves of each string as an integer. Take 1.1 and 2.1, for instance;
they compare as 1 and 2 would compare as integers. Very well, now take
1.10 and 1.2, for instance. 1 and 1 are the same, so 10 and 2 need to
compare as the integers 10 and 2. So:

arr = %w{
1.1
1.10
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
}
arr = arr.sort do |x,y|
x1, x2 = x.split(".")
y1, y2 = y.split(".")
if x1 != y1
x1.to_i <=> y1.to_i
else
x2.to_i <=> y2.to_i
end
end
p arr

If there are a lot of these, however, it would be much better to do a
keyed sort using sort_by. How to create keys depends on what you know
about the data. If we knew for a fact that the second half was always
between 1 and 99, it would be easy:

arr = arr.sort_by do |x|
x1, x2 = x.split(".")
x1.to_i * 100 + x2.to_i
end

If the second half can be any length, though, you'll have to determine
the maximum length first, and change that "100" multiplier accordingly.

m.


--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

Aaron Vegh

3/28/2009 2:58:00 AM

0

Hi Matt,
Are you _the_ Matt Neuburg, of AppleScript fame? It's a privilege to
have an answer from you!

matt neuburg wrote:

> These are not numbers; they're strings. Or at least, they need to be; if
> they weren't, 1.1 and 1.10 would be indistinguishable.

Yes, I think this was becoming my assumption. The head-scratcher for me
was trying to determine if this sort of numbering scheme ("the
hierarchical list", if you will) was a special case in Ruby or
generally. But in my model right now (this is for a Rails project) it's
set as a string.

> If there are a lot of these, however, it would be much better to do a
> keyed sort using sort_by. How to create keys depends on what you know
> about the data. If we knew for a fact that the second half was always
> between 1 and 99, it would be easy:
>
> arr = arr.sort_by do |x|
> x1, x2 = x.split(".")
> x1.to_i * 100 + x2.to_i
> end

And it turns out that a hundred is more than enough in this case. Thanks
to you, and to Yossef above, for a working solution.

Cheers!
Aaron.
--
Posted via http://www.ruby-....

Matthias Reitinger

3/28/2009 4:05:00 AM

0

matt neuburg wrote:
> If there are a lot of these, however, it would be much better to do a
> keyed sort using sort_by. How to create keys depends on what you know
> about the data. If we knew for a fact that the second half was always
> between 1 and 99, it would be easy:
>
> arr = arr.sort_by do |x|
> x1, x2 = x.split(".")
> x1.to_i * 100 + x2.to_i
> end
>
> If the second half can be any length, though, you'll have to determine
> the maximum length first, and change that "100" multiplier accordingly.

Either that or use arrays as sort keys:

arr = arr.sort_by do |x|
x.split(".").map {|i| i.to_i}
end

-Matthias
--
Posted via http://www.ruby-....

Nobuyoshi Nakada

3/28/2009 5:30:00 AM

0

Hi,

At Sat, 28 Mar 2009 13:04:43 +0900,
Matthias Reitinger wrote in [ruby-talk:332318]:
> > If the second half can be any length, though, you'll have to determine
> > the maximum length first, and change that "100" multiplier accordingly.
>
> Either that or use arrays as sort keys:
>
> arr = arr.sort_by do |x|
> x.split(".").map {|i| i.to_i}
> end

enc/depend file in 1.9 has more generic code.

alphanumeric_order = proc {|e| e.scan(/(\d+)|(\D+)/).map {|n,a| a||[n.size,n.to_i]}.flatten}

But this fails if strings start with digit and strings start
with non-digit are mixed. In such case, this is safer.

alphanumeric_order = proc {|e| e.scan(/(\A\D*|\G\D+)(\d+)/).map{|a,n|[a,n.size,n.to_i]}.flatten}

--
Nobu Nakada

Robert Klemme

3/28/2009 10:10:00 AM

0

On 28.03.2009 05:04, Matthias Reitinger wrote:
> matt neuburg wrote:
>> If there are a lot of these, however, it would be much better to do a
>> keyed sort using sort_by. How to create keys depends on what you know
>> about the data. If we knew for a fact that the second half was always
>> between 1 and 99, it would be easy:
>>
>> arr = arr.sort_by do |x|
>> x1, x2 = x.split(".")
>> x1.to_i * 100 + x2.to_i
>> end
>>
>> If the second half can be any length, though, you'll have to determine
>> the maximum length first, and change that "100" multiplier accordingly.
>
> Either that or use arrays as sort keys:
>
> arr = arr.sort_by do |x|
> x.split(".").map {|i| i.to_i}
> end

Here's a variant (1.9 only):

arr.sort_by {|x| x.scan(/\d+/).map(&:to_i)}

Cheers

robert

Chris Davies

3/28/2009 11:26:00 AM

0

Aaron Vegh <aaron@vegh.ca> wrote:
> I'm working with a list of items that contain decimal-based index
> numbers. I'm sorry I don't know the term for these kinds of numbers,
> and I'm having trouble finding out how to sort them properly [...]

Probably the Dewey Decimal Classification, or the Universal Decimal
Classification.

Chris

matt

3/28/2009 4:38:00 PM

0

Matthias Reitinger <reitinge@in.tum.de> wrote:

> matt neuburg wrote:
> > If there are a lot of these, however, it would be much better to do a
> > keyed sort using sort_by. How to create keys depends on what you know
> > about the data. If we knew for a fact that the second half was always
> > between 1 and 99, it would be easy:
> >
> > arr = arr.sort_by do |x|
> > x1, x2 = x.split(".")
> > x1.to_i * 100 + x2.to_i
> > end
> >
> > If the second half can be any length, though, you'll have to determine
> > the maximum length first, and change that "100" multiplier accordingly.
>
> Either that or use arrays as sort keys:
>
> arr = arr.sort_by do |x|
> x.split(".").map {|i| i.to_i}
> end

Yes, that's better; my first answer failed to take into account Array's
existing implementation of the spaceship operator. m.

--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

Yossef Mendelssohn

3/28/2009 10:24:00 PM

0

On Mar 27, 11:04=A0pm, Matthias Reitinger <reiti...@in.tum.de> wrote:
> =A0 arr =3D arr.sort_by do |x|
> =A0 =A0 x.split(".").map {|i| i.to_i}
> =A0 end

Durr, of course that's better than my solution of transforming the
array elements and then re-creating the original ones after the sort.
For some reason, I completely forgot about sort_by.

--
-yossef