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

Hi Monks! I want to sort an array in two different aspects. Alphabetically and numerically. For instance, this is my list:
erez[11] dana[22] dana[0] erez[10] erez[1] erez[0] dana[10]
And I want it to be sorted like:
dana[0] dana[10] dana[22] erez[0] erez[1] erez[10] erez[11]
For that I used:
my @new_list=sort {(($a) cmp ($b)) & ($a =~ /\[(\d+)/)[0]<=> ($b =~ /\ +[(\d+)/)[0]} @split_list;
But it is not done correctly. I also tried to replace the '&' with '||' but it didnt help. Any suggestions? Thanks, Erez.

Replies are listed 'Best First'.
Re: Sort options
by FunkyMonk (Bishop) on Aug 03, 2008 at 15:14 UTC
    ($a) cmp ($b) is comparing the full strings, not just the alphabetics, so perl never gets to the second comparison (which should be or'ed, BTW). You need something like

    my @split_list = qw{erez[11] dana[22] dana[0] erez[10] erez[1] erez[0] + dana[10]}; my @new_list = sort { ($a =~ /(\w+)/)[0] cmp ($b =~ /(\w+)/)[0] || ($a =~ /(\d+)/)[0] <=> ($b =~ /(\d+)/)[0] } @split_list; print "$_\n" for @new_list; __END__ dana[0] dana[10] dana[22] erez[0] erez[1] erez[10] erez[11]


    Unless I state otherwise, all my code runs with strict and warnings
      If the OPs list is allowed to have elements like i.e. dana1[23], then it would make sense to let the opening square bracket remain part of the regexp like it was before: (... =~ /\[(\d+)/)[0].
      Great! thanks. But what if I get my list from a file? How do I use the 'qw' function? I tried operating it on an array but it doesnt work...
      my @split_list = qw{@list};
      Is this correct?
        Did you first look for qw{} in the documentation/perl book/man pages before asking here?

        qw is a way to specify literal lists. @a= qw{ a b c } is equivalent to  @a= ('a','b','c');. If you already have an array, you don't need qw.

Re: Sort options
by pjotrik (Friar) on Aug 03, 2008 at 15:24 UTC
    #!/usr/bin/perl use warnings; use strict; my @list = qw(erez[11] dana[22] dana[0] erez[10] erez[1] erez[0] dana[ +10]); my @sorted = sort sorter @list; print join ' ', @sorted; sub sorter { my ($a_str, $a_num) = ($a =~ /(\w+).(\d+)/); my ($b_str, $b_num) = ($b =~ /(\w+).(\d+)/); return $a_str cmp $b_str unless $a_str eq $b_str; return $a_num cmp $b_num; }
    Just a quick one. It would probably be more efficient to run the regexp match only once and transform the list items into somthing like ({str => 'dana', num => 10},...).
      It would probably be more efficient to run the regexp match only once
      Maybe overkill here, but why not a Schwartzian?
      use strict; use warnings; my @split_list = qw( erez[11] dana[22] dana[0] erez[10] erez[1] erez[0] dana[10] ); print "Before: @split_list\n"; my @new_list = map { $_->[0] } sort { $a->[1][0] cmp $b->[1][0] or $a->[1][1] <=> $b->[1][1] } map { [ $_, [ (/(\w+).(\d+)/) ] ] } @split_list; print "After: @new_list\n";

      Before: erez[11] dana[22] dana[0] erez[10] erez[1] erez[0] dana[10] After: dana[0] dana[10] dana[22] erez[0] erez[1] erez[10] erez[11]
        Or a Guttman-Rossler, perhaps?

        use strict; use warnings; use List::Util q{max}; my @list = qw{ erez[11] arianne[2763] dana[22] bob[7] dana[0] erez[10] arianne[75] bob[37] erez[1] erez[0] bob[16] dana[10] }; my $longest = max map { length } map { m{(\w+)} } @list; my @sorted = map { substr $_, $longest + 4 } sort map { pack qq{A${longest}LA*}, m{(\w+)\[(\d+)}, $_ } @list; print qq{$_\n} for @sorted;

        produces

        arianne[75] arianne[2763] bob[7] bob[16] bob[37] dana[0] dana[10] dana[22] erez[0] erez[1] erez[10] erez[11]

        Cheers,

        JohnGG

Re: Sort options
by swampyankee (Parson) on Aug 03, 2008 at 17:45 UTC

    Clearly, the & (bitwise-and operator; did you mean &&, the logical and operator?) won't work. The logical || operator won't perform the second test unless both strings ($a and $b) on the left side of the or are identical, which is not the case for data[1] and data[10]. On the right side, regex are (at least potentially) expensive; you would probably want to avoid their use in the sort; for this see How do I do a complex sort on an array?. For sorting small arrays, using merlyn's Schwartzian Transform won't make a noticeable difference in performance (of course, my definition of "small" would be that using the Schwartzian Transform doesn't make a noticeable difference), but my experience is that anything being sorted is likely to grow past "small" as soon as a program is in the hands of its ultimate users.

    It's also possible that you would be better off using a different structure (HoA?) to store your data, but that may be only because I happen to have a morbid fascination with HoA's and HoH's, probably because my programming origins are in a language where the only data structures were arrays of various ranks.


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc