Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sorting Problem

Status
Not open for further replies.

lmbylsma

Programmer
Nov 11, 2003
33
US
I'm using the following to sort an array that is composed of a list of decimal numbers:

@Final = sort { $a cmp $b } @Final;


It almost does it right accept it thinks like 9.2 is the same thing as 90.2 or 900.2. Here's a sample of the output:


887.91732
89.58872
890.72645
894.04662
899.18190
9.98097
903.69055

How can I get it to take the decimal places into account?

Thanks,

Lauren
 
cmp does an ascii comparison, <=> does a numeric one. Since they're already numbers, it should perform a numeric sort by default (and does it more quickly without the custom sortsub).
Code:
@Final = sort @Final;
But if this is the same @Final mentioned in other threads with a long string of stuff after the initial decimal number, a more complicated sorting method will be needed.

----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
It's still sorting it the same way.

What it has after it just looks like this at this point in the script:

887.91732 1
89.58872 0
890.72645 1
894.04662 1
899.18190 0
9.98097 1
903.69055 0

 
I always have to create a numeric sort.

sub numeric { $a<=>$b; }

then

@array = sort numeric @start_array;

That forces perl out of a string context and into a numeric.
 
I did that and then it complains that its not numeric. Its not completely numeric because it has 2 numbers with a tab in between.
 
Oh, I thought you had already split it out.

If you source is this

number [tab] number

and you want to sort on the first one split each line, using the first element as a key into a hash.

Then sort the keys of the hash. Your original post did not state anything about the source format.

So something like

------
#!/usr/bin/perl

sub numerically { $a<=>$b; }

open( FILE , &quot;test.txt&quot; ) ;

my %hash ;
while(<FILE>){
chomp ;
my @line = split(/\t/ , $_);
$hash{$line[0]} = $line[1];
}

close(FILE);

my @Final = sort numerically keys %hash ;

foreach my $item(@Final ) {
print &quot;$item: $hash{$item}\n&quot; ;
}
----

assuming your source is coming from a file 'test.txt' that looks like :

-----
887.91732 1
89.58872 0
890.72645 1
894.04662 1
899.18190 0
9.98097 1
903.69055 0
------


This will produce this output

-----

9.98097: 1
89.58872: 0
887.91732: 1
890.72645: 1
894.04662: 1
899.18190: 0
903.69055: 0


----

Which is what I think you want??
 
I'd imagine that two of the first numbers could be the same (though sounds unlikely, considering the precision, but there's probably nothing stopping two to be the same). In that case, the second of the two would just overwrite the first, losing it.

I'm a big fan of the packed default sort (a twist on the Schwartzian Transform, I think Barbie posted it here six months or so ago, but I can't remember [1]), but I was having major issues making it work with floating point numbers. Apparently the way floats are stored in binary (all the varieties of floats that pack offered) they can't be lexically sorted. So the next best thing I could think of was just a formatted string with the number via sprintf. Seems to work okay, but if you require precision greater than available (or if any number will overflow it), be sure to increase $len. This will probably be the speediest way to sort it.
Code:
my $len = 15;
my $i = 0;
@Final = map { $Final[substr($_,$len)] }
         sort
         map { sprintf(&quot;%${len}s&quot;,(split /\t/)[0]).$i++ }
         @Final;
Actually, a quick search on CPAN sent me to Sort::Fields [2] which sounds exactly like what you're doing here. It's probably the best and easiest way to go, so long as you're still dealing with lists of delimited strings. Looks to be as easy as this (though I haven't tried it):
Code:
use Sort::Fields;
#break into fields on tabs, sort on the first field numerically
@Final = fieldsort /\t/, ['1n'], @Final;

[1] [2]
----------------------------------------------------------------------------------
...but I'm just a C man trying to see the light
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top