Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

How can I sort this data on the first field

by Anonymous Monk
on Apr 25, 2005 at 06:45 UTC ( #451109=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Need to sort data on first field numerically and print to browser.
print "Content-type: text/html\n\n"; @someData = <DATA> __DATA__ 30|microsoft 70|aol 76|netscape 35|mozilla 40|opera

Comment on How can I sort this data on the first field
Download Code
Re: How can I sort this data on the first field
by ColtsFoot (Chaplain) on Apr 25, 2005 at 06:55 UTC
    Take a look at the Sort::Fields package

    Sort::Fields provides a general purpose technique for efficiently sorting lists of lines that contain data separated into fields.
Re: How can I sort this data on the first field
by gaal (Parson) on Apr 25, 2005 at 06:57 UTC
    my @sorted = map { $_->[1] } # pick interesting data sort { $a->[0] <=> $b->[0] } # sort numerically on first field map { [ split /\|/, $_, 2 ] } # extract sortable field: $_->[0] i +s sortable, # $_->[1] i +s data <DATA>;

    This is a simple variant of the Schwartzian Transform. Read it upwards.

    In the original transform, the original data is kept around as one of the elements of @$_. Here I was assuming you didn't need to display the sort number — but if you did, then you need to do something slightliy different than a split.

Re: How can I sort this data on the first field
by prasadbabu (Prior) on Apr 25, 2005 at 06:57 UTC

    If i understood your question correctly, is this you want,

    while(<DATA>) { chomp; @cpy=(); @cpy = split(/\|/); $cpy{$cpy[0]} = [@cpy]; } print "$_->[0]\t$_->[1]\n" for sort {$a->[0] <=> $b->[0]} values %cpy;
    output: 30 microsoft 35 mozilla 40 opera 70 aol 76 netscape

    updated:

    Prasad

      Thank you very much Prasad!
Re: How can I sort this data on the first field
by tlm (Prior) on Apr 25, 2005 at 06:59 UTC
    print "<table>\n"; print for map sprintf( "<tr><td>%d</td><td>%s</td></tr>\n", @$_ ), sort { $a->[ 0 ] <=> $b->[ 0 ] } map { chomp; [ split /\|/ ] } <DATA>; print "</table>\n";

    the lowliest monk

Re: How can I sort this data on the first field
by TedPride (Priest) on Apr 25, 2005 at 07:29 UTC
    I know there are several working examples above, but they all seem to be longer, more obfuscated, or don't include the creation code for the array. Props to the guy with the table code, though :) Here's mine.
    use strict; use warnings; my ($c, @data); while (<DATA>) { chomp; $data[$c++] = [split /\|/]; } print join('|', @$_) . "\n" for (sort {@$a[0] <=> @$b[0]} @data); __DATA__ 30|microsoft 70|aol 76|netscape 35|mozilla 40|opera

      The funny thing is that your version can be extremely shortened to this:

      print sort <DATA>; __DATA__ 30|microsoft 70|aol 76|netscape 35|mozilla 40|opera

      He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
      Chady | http://chady.net/
      Are you a Linux user in Lebanon? join the Lebanese Linux User Group.

        The example can, but as soon as you have numbers that are a different order of magnitude, it will fail:

        print sort <DATA>; __DATA__ 9|microsoft 70|aol 108|netscape 35|mozilla 40|opera

        Based on the similarity of this example, with that in Need help with searching flatfile and updating it., I can only assume that the numbers aren't going to remain static.

Re: How can I sort this data on the first field
by salva (Monsignor) on Apr 25, 2005 at 08:35 UTC
    use Sort::Key;
    my @sorted = ikeysort { /^(\d*)/; $1 } @data;
    
    or
    my @sorted = ikeysort { no warnings; int $_ } @data;
    or even
    my @sorted = sort { no warnings; $a <=> $b } @data;

    update: oops, "no warnings" can not be used inside the comparison expression, so...

    my @sorted; { no warnings; @sorted = sort { $a <=> $b } @data; }
    ... has to be used instead
Re: How can I sort this data on the first field
by sh1tn (Priest) on Apr 25, 2005 at 09:31 UTC
    One short sorting step:
    my %sort = map{ /^(\d+)\|(\w+)/ and $1, $2 } <DATA>; print "$_ - $sort{$_} \n" for sort keys %sort;


Re: How can I sort this data on the first field
by TedPride (Priest) on Apr 25, 2005 at 10:22 UTC
    Chady: Yes, but that only works if the number remains exactly two characters long. I highly doubt that is the case.
      What if you use print sort {$a<=>$b} <DATA>;?
      chas
      (Update: Posted after reading an unrefreshed page and didn't realize Roy Johnson had already suggested the same thing.)
Re: How can I sort this data on the first field
by Roy Johnson (Monsignor) on Apr 25, 2005 at 12:16 UTC
    Since your numbers are the initial field, and are terminated by non-numbers, you can just treat the whole string as a number and Perl will Do The Right Thing:
    print sort {$a <=> $b} <DATA>; __DATA__ 30|microsoft 70|aol 76|netscape 5|trouble? 35|mozilla 40|opera
    No need for splitting or Schwartzian Transforms.

    Caution: Contents may have been coded under pressure.
      Roy Johnson's solution works, but it is not warning free. The original problem seems to me to scream out for the use of a hash. Here is how I would have approached the problem:
      use strict; use warnings; print "Content-type: text/html\n\n"; my %someData; while(<DATA>) { chomp(); (my $key, my $value) = split(/\|/, $_, 2); $someData{$key} = $value; } foreach (sort { $a <=> $b } keys %someData) { print "$_\{$someData{$_}\}\n"; } __DATA__ 30|microsoft 70|aol 76|netscape 5|trouble? 35|mozilla 40|opera 100|FireFox

        It's not a hash that you want... it's probably an array of arrays. What if there were duplicate keys? your hash will eat them up and you'll end up with only the last entry.

        compare:
        print "With Hash:\n\n"; $save = tell DATA; while(<DATA>) { my ($k, $v) = split '\|'; $hash{$k} = $v; } print "$_|$hash{$_}" for sort { $a <=> $b } keys %hash; seek DATA, $save, 0; print "\n\nWith array:\n\n"; while(<DATA>) { my ($k, $v) = split '\|'; push @arr, [$k, $v]; } print "$_->[0]|$_->[1]" for sort { $a->[0] <=> $b->[0] } @arr; __DATA__ 30|microsoft 70|aol 76|netscape 5|trouble? 35|mozilla 30|die microsoft 40|opera 100|FireFox 5|no more trouble

        He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
        Chady | http://chady.net/
        Are you a Linux user in Lebanon? join the Lebanese GNU/Linux User Group.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://451109]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (7)
As of 2014-08-22 22:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (168 votes), past polls