Re: Sort array according to a value in each element?
by BrowserUk (Patriarch) on May 24, 2004 at 18:00 UTC
|
#! perl -slw
use strict;
my @array = split '\n', <<'EOA';
Item1 - 2 foo, 2 bar
Item2 - 0 foo, 1 bar
Item3 - 1 foo, 3 bar
Item4 - 1 foo, 2 bar
EOA
my @sorted = map{
substr $_, 5;
} sort map{
sprintf '%05d%s',
$_ =~ m[,\s+(\d+)], $_;
} @array;
print for @sorted;
__END__
Item2 - 0 foo, 1 bar
Item1 - 2 foo, 2 bar
Item4 - 1 foo, 2 bar
Item3 - 1 foo, 3 bar
Update: Limbic~Region pointed out that you wanted descending not ascending. so substitute sort { $b cmp $a } for sort or use reverse on the results.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [d/l] [select] |
|
|
I gotta like two working solutions with examples demonstrating the Schwartzian Transform and Guttman Rosler Transform...
Now, having hardly any background in Computer Science-- bubble sorts are about my speed =)-- I have to woodshed for a while to figure out *why* they work-- and how to clean up a couple of warnings.
Thanks L~R and BrowserUK!
| [reply] |
|
|
| [reply] |
Re: Sort array according to a value in each element?
by Limbic~Region (Chancellor) on May 24, 2004 at 17:58 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
my @array = (
'Item1 - 2 foo, 2 bar',
'Item2 - 0 foo, 1 bar',
'Item3 - 1 foo, 3 bar',
'Item4 - 1 foo, 2 bar',
);
my @sorted =
map { $_->[0] }
# $b <=> $a for descending is better than reverse sort
sort { $b->[2] <=> $a->[2] || $b->[1] <=> $a->[1] }
map { [$_, / (\d+) [^\d]+(\d+)/] } @array;
print "$_\n" for @sorted;
Cheers - L~R | [reply] [d/l] |
|
|
my @list= <DATA>;
@list= @list[
map { unpack "N", substr($_,-4) }
sort
map {
# Only these two lines had to be written:
my( $foo, $bar )= $list[$_] =~ / (\d+) /g;
~pack("N",$bar) . pack("N",$foo)
. pack "N", $_
} 0..$#list
];
print @list;
__END__
Item1 - 2 foo, 2 bar
Item2 - 0 foo, 1 bar
Item3 - 1 foo, 3 bar
Item4 - 1 foo, 2 bar
Item4 - 3 foo, 2 bar
Item4 - 0 foo, 2 bar
Yes, this screams to be put into a module... (:
Rather than ~pack"N", you can also use ~sprintf"%09d" or even ~(length($n).$n)."\0" if you are dealing with non-negative integers, but pack"N" is nice in that it handles negative integers and rather large numbers and is fast.
| [reply] [d/l] |
Re: Sort array according to a value in each element?
by jZed (Prior) on May 24, 2004 at 18:52 UTC
|
I'm not certain how to approach this problem: split or regex? hash or sort()?
I'd add 'databases' to that list of possible solutions although for most circumstances the other answers in this thread are better.
#!perl -w
use strict;
use DBI;
my $aryref = [ ['Item1','2 foo','2 bar']
, ['Item2','0 foo','1 bar']
, ['Item3','1 foo','3 bar']
, ['Item4','1 foo','2 bar']
];
my $dbh=DBI->connect('dbi:AnyData:');
$dbh->ad_import('tmp','ARRAY',$aryref,{col_names=>'c1,c2,c3'});
printf "%s\n",join ' ',@$_ for @{ $dbh->selectall_arrayref(
'SELECT * FROM tmp ORDER BY c3 DESC, c2 DESC'
)};
__END__
| [reply] [d/l] |
|
|
jZed, my first thought was "this would be easy if it were SQL".
But the example data is really brain-dead; the real data is interspersed throughout a gigantic text file, and normalizing it for a database isn't really an option.
Update: jZed's reply is right on. I didn't read closely enough. In fact, I didn't realize DBI was capable of treating an arrayref as a database and getting to it via SQL.
I'm knee-deep in the Guttman/Rosler paper at the moment (good information there), but this is definitely worth exploring. Soon.
| [reply] |
|
|
the real data is interspersed throughout a gigantic text file, and normalizing it for a database isn't really an option.
I'm sure you're right. But look at my example again. No file or external database is created and no normalizing is done at all. It just takes the same $arrayref you started with and creates a temporary-in-memory table and allows you to query the $arrayfef as if it were a database.
| [reply] |
Re: Sort array according to a value in each element?
by Art_XIV (Hermit) on May 24, 2004 at 20:25 UTC
|
Although the sort function is un-glamorous and not very fast, there are times when it is adequate for the task at hand:
use strict;
use warnings;
my @array = (
'Item1 - 2 foo, 2 bar',
'Item2 - 16 foo, 8 bar',
'Item3 - 0 foo, 1 bar',
'Item4 - 1 foo, 3 bar',
'Item5 - 4 foo, 12 bar',
'Item6 - 1 foo, 2 bar',
);
@array = sort {get_bar($b) <=> get_bar($a)} @array;
print "$_\n" for @array;
sub get_bar {
my ($line) = @_;
my ($bar) = $line =~ /, (\d+) bar/;
return $bar;
}
There is no doubt that one of the transform methods would be much faster, though.
Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"
| [reply] [d/l] [select] |
Re: Sort array according to a value in each element?
by dimar (Curate) on May 25, 2004 at 03:53 UTC
|
I have an array whose elements look like this:
Yes, on the surface its an array, but conceptually, it seems like you are working with a three column table, with dashes and commas as your field separators.
If my inference is correct, then you will probably want the ability to sort on *any* column, not just the 'bar' column. With this in mind, the following code offers a way to manage your data more like a table. You can sort it, format it, select for individual fields and do all the other stuff people always ask for, but didn't realize they wanted at the beginning.
If my inference is wrong, you can ignore this post.
### INIT pragma
use strict;
use warnings;
### INIT vars
my $data_table = [];
my @aLines = (<DATA>);
### MUNGE raw data into a table
@{$data_table} = map{
my @aFlds = split /\s*-\s*|\s*,\s*/,$_;
if(scalar @aFlds != 3){die"ERROR: bad data"}
chomp$aFlds[2];
{fld0 => $aFlds[0], fld1 => $aFlds[1], fld2 => $aFlds[2],};
}@aLines;
### SORT the table or do whatever else you want
### See Also 4.15. Sorting a List by Computable Field, Perl Cookbook
### By Tom Christiansen & Nathan Torkington; ISBN 1-56592-243-3
my @ordered = sort { $a->{fld2} cmp $b->{fld2} } @{$data_table};
map{print "$_->{fld0} ;;; $_->{fld1} ;;; $_->{fld2}\n"}@ordered;
__DATA__
Item3 - 1 foo, 3 bar
Item1 - 2 foo, 2 bar
Item4 - 1 foo, 2 bar
Item2 - 0 foo, 1 bar
| [reply] [d/l] |
Re: Sort array according to a value in each element?
by ishnid (Monk) on May 25, 2004 at 10:57 UTC
|
#!/usr/bin/perl -w
use strict;
use Sort::Fields;
my @array = ('Item1 - 2 foo, 2 bar',
'Item2 - 0 foo, 1 bar',
'Item3 - 1 foo, 3 bar',
'Item4 - 1 foo, 2 bar'
);
my @sorted = fieldsort ['-5n', '-3n'], @array;
print "$_\n" for @sorted;
| [reply] [d/l] |
Re: Sort array according to a value in each element?
by Anonymous Monk on May 24, 2004 at 20:19 UTC
|
@arr = ();
$arr[0][0]=2;
$arr[0][1]=2;
$arr[1][0]=0;
$arr[1][1]=1;
$arr[2][0]=1;
$arr[2][1]=3;
$arr[3][0]=1;
$arr[3][1]=2;
foreach $val (sort {$b->[1] <=> $a->[1]} @arr)
{
print join("|", @{$val}) . "\n";
}
You can also implement this using hashes.
Edit by castaway, swapped pre tags for code tags | [reply] [d/l] |
|
|
At first I thought this was an attempt at symbolic references, but upon further inspection it doesn't even compile. Either you're completely clueless about Perl's array syntax, or you've misused the markup tags somehow.
| [reply] |
Re: Sort array according to a value in each element?
by deibyz (Hermit) on May 25, 2004 at 10:43 UTC
|
If array data is like you show, you only have to sort it starting from the end.
You can do it like that:
my @items= (
'Item3 - 1 foo, 3 bar',
'Item1 - 2 foo, 2 bar',
'Item4 - 1 foo, 2 bar',
'Item2 - 0 foo, 1 bar'
);
my @sorted = reverse sort map{scalar reverse $_}@items;
print scalar reverse $_ . "\n" foreach @sorted;
I know the rest of the options are better, faster and more flexible, but I think this is simplier.
| [reply] [d/l] |
Re: Sort array according to a value in each element?
by Anonymous Monk on May 25, 2004 at 15:46 UTC
|
I'm sorry, I just had to do it in one line. ;-)
my @array = (
'Item1 - 1 foo, 2 bar',
'Item2 - 16 foo, 8 bar',
'Item3 - 0 foo, 1 bar',
'Item4 - 1 foo, 3 bar',
'Item5 - 4 foo, 12 bar',
'Item6 - 2 foo, 2 bar',
);
@sortedArray =
sort {($b=~/(\d+) bar/)[0] <=> ($a=~/(\d+) bar/)[0]} @array;
$"="\n";
print "@sortedArray\n";
# sort by two fields
@sortedArray = sort {
($b=~/(\d+) bar/)[0] <=> ($a=~/(\d+) bar/)[0] ||
($b=~/(\d+) foo/)[0] <=> ($a=~/(\d+) foo/)[0]
} @array;
print "\n@sortedArray\n";
| [reply] [d/l] |