fufaso has asked for the wisdom of the Perl Monks concerning the following question:

Hey all, Brand new to Perl and am having a great time, but am stumped with this problem. I have an inventory list broken up like this:
YEAR MAKE MODEL PRICE 2003 abc rel 999 1999 hds sdf 100 2010 kls pol 1400
What I am doing is storing that list as an array:
@inventory=('2003 abc rel 999', '1999 hds sdf 100', '2010 kls pol 1400');
I want to run the program and take a user input that says YEAR, MAKE, PRICE and sort it accordingly. Here's what I've tried, but was not right was it is separating the columns and storing them together as oppose to sorting by column while keeping the rows intact.
$lookfor=<STDIN>; chop($lookfor); foreach $inv (@inventory) { ($YEAR,$MAKE,$MODEL,$PRICE)=split(/ /, $inv); push (@year,$YEAR); push (@make, $MAKE); push (@price, $PRICE); } if ($lookfor =~ /year/i){ @sorted = sort @year; print join "\n",@sorted; print "\n"; } if ($lookfor =~ /make/i){ @sorted = sort @make; print join "\n", @sorted; print "\n"; }
Any tips on how to make this work would be great!

Replies are listed 'Best First'.
Re: Inventory List Sorting Question
by GrandFather (Saint) on Sep 23, 2010 at 00:51 UTC

    There are a few things that can be learned here. First off, always use strictures (use strict; use warnings;).

    Next, avoid wherever possible handling records using parallel arrays. There are many ways to do that, but in this case I suggest using an array of hashes - you'll see why in a moment.

    Where you have named data try to use the name in your code rather than translating it into an index and simply using the index everywhere. It makes it much easier to see what the code is doing!

    Don't use chop. Use chomp instead, it's more likely to do what you expect.

    Because you want to sort by an arbitrary field in your data it's easiest if you can directly access the fields by name. That implies a hash. The following sample code populates an array of items with records that are hashes containing an entry for each field. Don't spend too much time puzzling over how @items gets populated or @sorted gets printed. The key parts here are the sort and the data structure.

    use strict; use warnings; my @items; while (<DATA>) { chomp; my ($year, $make, $model, $price) = split; push @items, {year => $year, make => $make, model => $model, price => $pric +e}; } my $lookFor = lc <STDIN>; chomp $lookFor; die "year, make, model or price expected\n" if $lookFor !~ /^(year|make|model|price)$/; my @sorted = sort {$a->{$lookFor} cmp $b->{$lookFor}} @items; printf "%4s %-6s %-6s %6s\n", @{$_}{qw(year make model price)} for @so +rted; __DATA__ YEAR MAKE MODEL PRICE 2003 abc rel 999 1999 hds sdf 100 2010 kls pol 1400
    True laziness is hard work
Re: Inventory List Sorting Question
by BrowserUk (Patriarch) on Sep 22, 2010 at 22:41 UTC

    The first thing to do is to split your data into an array of arrays.

    As your data doesn't contain any duplicated values, this doesn't show that it will correctly sort by two or more fields, but it will. Translating column names to numbers is left as an exercise.

    #! perl -slw use strict; my @inventory = ( [ qw'2003 abc rel 999' ], [ qw'1999 hds sdf 100' ], [qw'2010 kls pol 1400' ], );; while( my @order = split ' ', <STDIN> ) { print "@$_" for sort{ my $r; for(@order) { if( $a->[ $_ ] =~ m[^\d+$] && $b->[ $_ ] =~ m[^\d+$] ) { ( $r = $a->[ $_ ] <=> $b->[ $_ ] ) and last; } else { ( $r = $a->[ $_ ] cmp $b->[ $_ ] ) and last; } } $r } @inventory; } __END__ c:\test>junk42 0 1999 hds sdf 100 2003 abc rel 999 2010 kls pol 1400 1 2003 abc rel 999 1999 hds sdf 100 2010 kls pol 1400 2 2010 kls pol 1400 2003 abc rel 999 1999 hds sdf 100 3 1999 hds sdf 100 2003 abc rel 999 2010 kls pol 1400 1 2 2003 abc rel 999 1999 hds sdf 100 2010 kls pol 1400 ^Z

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Inventory List Sorting Question
by Anonymous Monk on Sep 22, 2010 at 22:48 UTC
    #! /usr/bin/env perl use Modern::Perl; use strict; use warnings; use feature qw( say state ); # Perl 5.10.0 or greater my @inventory = split /\R/, "YEAR MAKE MODEL PRICE 2003 abc rel 999 1999 hds sdf 100 2010 kls pol 1400"; my %columns = map { state $c = 0; lc, $c++ } map { split } (my $header = shift @inventory); print "sort by? "; chomp(my $lookfor = lc <STDIN>); die "bad sort criteria" unless exists $columns{$lookfor}; my $sortcol = $columns{$lookfor}; say for $header, sort { if (grep { /\D/ } (split ' ', $a)[$sortcol], (split ' ', $b)[$ +sortcol]) { (split ' ', $a)[$sortcol] cmp (split ' ', $b)[$sortcol] } else { (split ' ', $a)[$sortcol] <=> (split ' ', $b)[$sortcol] } } @inventory;
      I got it sorted, and did something a little different. Is there a benefit to doing it one way over the other? Here's my code:
      $lookfor=<STDIN>; chop($lookfor); if ($lookfor =~ /year/i){ for $list_ref1 ( sort { $a->[0] cmp $b->[0] } @inventory ) { print "\t [ @$list_ref1 ],\n"; } } if ($lookfor =~ /make/i){ for $list_ref1 ( sort { $a->[1] cmp $b->[1] } @inventory ) { print "\t [ @$list_ref1 ],\n"; } }