in reply to Sorting an array on two computed fields

Something like this?

my @sorted = map { $_->[2] } sort { $a->[0] <=> $b->[0] or $a->[1] <=> $b->[1] } map { [ split( /-/, $_ ), $_ ] } @unsorted;

...untested, but it ought to work. The biggest problem is figuring out the best use of whitespace in making such a construct look pretty.

Update: For the record, I just tested it, and yes, it works as advertised. ;) The Schwartzian Transform isn't really a necessary part of it all, but to me, it makes it easier to read than if I had put all the splitting in the sort code block. Also, the or is used for "fall through". If the first comparison evaluates to equality, the logic falls through to the second comparison.

Update-2: Here is it without the Schwartzian Transform. ...maybe it's not that ugly after all, though as the data set grows it may become less efficient:

my @sorted = sort { my @a = split /-/, $a; my @b = split /-/, $b; $a[0] <=> $b[0] or $a[1] <=> $b[1] } @unsorted;

Dave

Replies are listed 'Best First'.
Re^2: Sorting an array on two computed fields
by blazar (Canon) on Dec 16, 2005 at 11:17 UTC
    Well, still using map, this seems a good candidate for Guttman-Rossler, and such an approach is also compatible with jesuashok's new requirement:
    #!/usr/bin/perl -l use strict; use warnings; my @data=qw/1-1 6 2-5 1-10 7 1-3 2-1/; print for map +(split)[1], sort map { (my $s=$_) =~ s/(\d+)/sprintf '%06d', $1/ge; "$s $_"; } @data; __END__

    Of course this is exclusively for illustrational purposes. In more complex or realistic situations he will have to find a suitable separator, provided that this is possible, and maybe he will have to work with index and substr instead, although pattern matching should be already optimized on fixed strings (as opposed to "real" patterns).

    Oh, and of course he must know in advance that the size of his numbers has an upper bound, which seems to be the case.

Re^2: Sorting an array on two computed fields
by tphyahoo (Vicar) on Dec 16, 2005 at 18:36 UTC
    it may become less efficient...

    It may, or it will? I guess what I am asking is, when does it pay to do the S.T. and why? Does it pay in this case? (I mean, if the data set was a lot larger?)

    UPDATE: Maybe an answer at Re: When does it pay to use the schwartzian transform?

Re^2: Sorting an array on two computed fields
by jesuashok (Curate) on Dec 16, 2005 at 10:35 UTC
    sorry for changing the requirement again what will happen if the array contains as follows:-
    my @array = ( '1-1', '6', '2-5', '1-10', '7', '1-3','2-1');
    "Keep pouring your ideas"

      ...expanding on my version "without Schwartzian Transform":

      my @sorted = sort { my @a = split /-/, $a; my @b = split /-/, $b; $a[0] <=> $b[0] or ( defined( $a[1] ) && defined( $b[1] ) and $a[1] <=> $b[1] ) } @unsorted;

      As before, the worst part is figuring out how to tab the whole thing to keep it readable. ;) You need the defined test so that fall-through doesn't occur on single-term items.

      Update: Hmm, there is a problem here though. You didn't define whether "6" should come before, after, or between "6-0" and "6-10". My solution didn't define it either, which means that the definition of the problem is inadequate, and that the solution is equally inadequate. But the point to all this is that you can hand-craft your sort routine. ...just think through what you want it to do, and craft your code. I need to get some sleep, so I'll leave you in the capable hands of the rest of the PerlMonks. :) You can do it, really. Dig in and let us know when you get stuck.


      Dave

      sorry for changing the requirement again what will happen if the array contains as follows:
      my @array = ( '1-1', '6', '2-5', '1-10', '7', '1-3','2-1');
      Well, then it depends on how you want '6' to compare to the other entries. If it's the ehm natural one, then again Sort::Naturally should be appropriate!