Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Re: Custom Sort An AoA

by kennethk (Abbot)
on Apr 01, 2014 at 16:34 UTC ( [id://1080600]=note: print w/replies, xml ) Need Help??


in reply to Custom Sort An AoA

Seems like this should be straightforward:
my @list = sort { @$a <=> @$b or join(' ', reverse @$a) cmp join(' ', reverse @$b) } @input;

Does that fit the bill? Seems like my understanding of the spec is inconsistent with the other implementations, so I don't know if I've missed something.

If the array operations turn pricey, you could optimize with an Orcish Maneuver.


#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Replies are listed 'Best First'.
Re^2: Custom Sort An AoA
by Limbic~Region (Chancellor) on Apr 01, 2014 at 17:28 UTC
    kennethk,
    This will work and that's what matters. Can you think of a more general purpose solution though (consider if the elements had been numerical and what was desired was numerical order for instance).

    Cheers - L~R

      If all the data were numeric and limited in length, you could zero-pad it before the join and then CMP it since <=> will probably explode on longer arrays.

      If you need it to be fully generic, you could always break down and make the sort block be a complex sub that loops over the elements and returns once it finds a difference.

      sub sortIt { my $result = @$a <=> @$b; my $idx = $#$a; while (!$result && $idx >=0) { $result = $a->[$idx] cmp $b->[$idx]; $idx--; } return $result; }
        I'd do it too with an extra helper function, but I would keep the first condition out (for readability, flexibility and reuse)

        something like

        sub cmp_vec { # return either -1,0 or 1 comparing over two equally sized arrays } sort { @$a <=> @$b or cmp_vec($a,$b) } @list;

        for full flexibility a cmp-function could be passed as call-back in third position.

        As a side note, I'm not sure if this could be done with Perl6 Meta ops, something like @a >>cmp<< @b (after reversing of course).

        Cheers Rolf

        ( addicted to the Perl Programming Language)

        update

        something like

        sub cmp_vec { my ($a,$b) = @_; for (reverse 0.. @$a-1) { if (my $x= $a->[$_] cmp $b->[$_]) { return $x } } return 0; ]
        SuicideJunkie,
        ...and limited in length....

        You would also need to know that limit and pad everything at every level or else it would still be very complex.

        If you need it to be fully generic...

        Ah yes, this is along the lines of what I was looking for.

        Cheers - L~R

      Lots of discussion has ensued since I checked out yesterday, but the most generic treatment I can think of is essentially pre-computing the comparison strings in two passes, so that you can be sure that your arguments are properly conditioned:
      #!/usr/bin/perl use strict; use warnings; my @input = ( ['blah', 'asdf', 'foo', 'bar'], ['two'], ['zzz', 'def', 'ghi'], ['one'], ['mmm', 'def', 'ghi'], ['qqq', 'xyz', 'aaa'], ); my @list = do { my $max_array = 0; my $max_word = 0; for (@input) { $max_array = @$_ if @$_ > $max_array; for (@$_) { $max_word = length if length > $max_word; } } my $digit = 1 + int log($max_array)/log(10); my $format = "%$digit.d" . ("%-${max_word}s") x $max_array; my %cache = map {$_ => sprintf $format, 0+@$_, reverse(@$_), ( +'') x $max_array} @input; sort {$cache{$a} cmp $cache{$b}} @input; }; $" = "', '"; print "['@$_']\n" for @list;
      My measurement approach means that it's no longer sensitive to delimiter choice, but of course this is a conservative approach to that since it assumes one $max_word for all terms. The empty string padding in the sprintf is just to silence warnings.

      If you want a numerical sorting for elements in place of lexical sorting, you could swap "%-${max_word}s" in the format constructor to "%${max_word}.d" More complex the pattern, the more complex the construction - you can see how impressively the code exploded for just these changes. You could even support a mixed mode by flopping between "%-${max_word}s" and "%${max_word}.d" depending on looks_like_number EXPR.


      #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re^2: Custom Sort An AoA
by SuicideJunkie (Vicar) on Apr 01, 2014 at 17:31 UTC

    Sounds like that could fail on cases where there is a space in the data.

    But as long as you can find a separator value that does not occur, you should be good.

      True, but I think that most people would feel uncomfortable with join chr(1), ... See Re^3: Custom Sort An AoA for a more rigorous solution, the complexity of which makes my first suggestion feel very inviting.

      #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1080600]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-24 04:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found