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

Thank you chromatic for answering my last question. I am working with a frameworks that one of the developers had made, so I don't have a lot of flexibility in declaring the input and output parameters. So what I need to do is sort an array that has been created using the following code:
while(<INPUT_FILE>) { my ($symbol,$side,$account,$shares) = split(','); if ($side eq 'BL') { $side = 'BUY'; { push(@$outputRef, "$symbol,$side,$account,$shares") }
So I tried using the following:
my (@sorted) = sort{ $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] || $a->[2] cmp $b->[2] } @$outputRef;
When I try to run it, i get an error that says:
"Can't use string "IBM,BUY,K226110,2700" as an ARRAY ref while "strict + refs" in use...

It also doesn't like it when I try to take out the "$" from the @$outputRef.

I need to sort this array by $symbol,$side,$account. Is there any way around this?

Replies are listed 'Best First'.
Re: Sorting a referrenced array and need to sort by specific fields.
by thpfft (Chaplain) on Jul 15, 2002 at 02:39 UTC

    The error message is right. Instead of this, which makes a string:

    push(@$outputRef, "$symbol,$side,$account,$shares");

    you need this, which makes an arrayref:

    push(@$outputRef, [ $symbol,$side,$account,$shares ]);

    or to make everything more readable further down:

    push(@$outputRef, { symbol => $symbol, side => $side, account => $account, shares => $shares, });

    But if that's the code you can't change, you'll have to split the strings first:

    my @sorted = sort{ $a->[1] cmp $b->[1] || $a->[0] cmp $b->[0] || $a->[2] cmp $b->[2] } map { [split(',', $_)] } @$outputref;

    update: removed mistaken notion of using a simple string sort.

Re: Sorting a referrenced array and need to sort by specific fields.
by lachoy (Parson) on Jul 15, 2002 at 02:32 UTC

    You need a custom sort procedure. The following should work:

    sub custom_sort { my @a_parts = split /,/, $a; my @b_parts = split /,/, $b; return $a_parts[0] cmp $b_parts[0] || $a_parts[1] cmp $b_parts[1] || $a_parts[2] cmp $b_parts[2]; } # To invoke: my @sorted = sort custom_sort @{ $outputRef };

    This would be easier if \@outputRef used arrayrefs for members rather than strings. But like you said, you have no control over that.

    Chris
    M-x auto-bs-mode

      You don't need a sort procedure - a block will do fine.

      But in the given sort procedure, you are performing two splits on each comparison. That's quite costly. It's much better to use either a Schwartzian transform, or a Guttman-Rosler-Transform. The former is the simpler, the latter the faster of the two.

      Here's the ST:

      @sorted = map {$_ -> [0]} sort {$a -> [1] cmp $b -> [1] || $a -> [2] cmp $b -> [2] || $a -> [3] cmp $b -> [3]} map {[$_ => split /,/]} @$outputRef;
      And here's the GRT:
      my ($max_symbol, $max_side, $max_account) = (0, 0, 0); foreach (@$outputRef) { my ($symbol, $size, $account) = split /,/; $max_symbol = length $symbol if length $symbol > $max_symbo +l; $max_size = length $size if length $size > $max_size + ; $max_account = length $account if length $account > $max_accou +nt; } my $total = $max_symbol + $max_size + $max_account; @sorted = map {substr $_, $total} sort map {my ($symbol, $size, $account) = split /,/; $symbol .= "\0" x $max_symbol; $size .= "\0" x $max_size; $account .= "\0" x $max_account; substr ($symbol, 0, $max_symbol) . substr ($size, 0, $max_size) . substr ($account, 0, $max_account) . $_} @$outputRe +f;
      The reason why the GRT is faster than ST is because the GRT doesn't have a custom sort routine, where ST does. GRT does more pre-processing work than ST, but that's only linear in the amount of elements to sort, while there will be O (N * log N) comparisons made.

      Abigail

Re: Sorting a referrenced array and need to sort by specific fields.
by shotgunefx (Parson) on Jul 15, 2002 at 07:49 UTC
    As noted by thpfft noted, it's not an array ref. If you can't change the part of the program that does the pushing and are doing strictly string compares, I would do the following for efficiency. (But personally I would just push an arrary ref instead of a string or at least explode them into array refs, do all processing and then join them back.)
    my @sorted = map { $_->[1] } # Lose the keys and return just original string sort { $a->[1] cmp $b->[1] } # Compare the keys # Make a temp array with [0] = original string, [1] a combin +ation of desired fields (key). map { [$_, (sprintf("%-5s%-8s%-8s" , split (/\s*,\s*/) )) ] } @{$outputRef};
    The sprintf just joins the fields into one big padded string. You would want to change the %-5s to whatever number of chars is appropriate for the data.

    -Lee

    "To be civilized is to deny one's nature."