in reply to Complex conditional sort

Do a Schwartzian Transform to capture the color code and a boolean to indicate whether there is a count, then sort, and finally unpack and print.
#!/usr/bin/perl -w use strict; print map {$_->[2]} sort {$b->[1] <=> $a->[1] || $a->[0] <=> $b->[0]} map {/(\d+).*?(\d+)/ ? [$1, !!$2, $_] : ()} <DATA>; 1; __DATA__ Colour Quantity 70 (Grey brown mix) 3 72 (Green mix) 10 74 (Fuschia mix) 8 76 (Mauve mix) 11 77 (Blue mix) 6 90 (Beige pink mix) 12 91 (Peach cream mix) 8 92 (Silver cream mix) 10 93 (Beige blue mix) 9 78 (Black white mix) 0 75 (Red mix) 0 73 (Aqua mix) 0 71 (Beige mix) 0
Outputs
70 (Grey brown mix) 3 72 (Green mix) 10 74 (Fuschia mix) 8 76 (Mauve mix) 11 77 (Blue mix) 6 90 (Beige pink mix) 12 91 (Peach cream mix) 8 92 (Silver cream mix) 10 93 (Beige blue mix) 9 71 (Beige mix) 0 73 (Aqua mix) 0 75 (Red mix) 0 78 (Black white mix) 0

Update: L~R pointed out that I did not use the initial data structure that you described, and therefore my result wasn't as helpful. Unfortunately, you do not state what actually are the keys of the third level hash. Assuming they are the colour codes and the names are inside the hash with Qua, then the following would work:

#!/usr/bin/perl -w use strict; my %products; $products{Cat}{Pro} = { 70 => {Name => 'Grey brown mix', Qua => 3}, 72 => {Name => 'Green mix', Qua => 10}, 74 => {Name => 'Fuschia mix', Qua => 8}, 76 => {Name => 'Mauve mix', Qua => 11}, 77 => {Name => 'Blue mix', Qua => 6}, 90 => {Name => 'Beige pink mix', Qua => 12}, 91 => {Name => 'Peach cream mix', Qua => 8}, 92 => {Name => 'Silver cream mix', Qua => 10}, 93 => {Name => 'Beige blue mix', Qua => 9}, 78 => {Name => 'Black white mix', Qua => 0}, 75 => {Name => 'Red mix', Qua => 0}, 73 => {Name => 'Aqua mix', Qua => 0}, 71 => {Name => 'Beige mix', Qua => 0}, }; foreach my $colour ( sort {!!$products{Cat}{Pro}{$b}{Qua} <=> !!$products{Cat}{Pro}{$a} +{Qua} || $a <=> $b} keys %{$products{Cat}{Pro}} ) { printf "%-2s %-22s %s\n", $colour, "($products{Cat}{Pro}{$colour}{ +Name})", $products{Cat}{Pro}{$colour}{Qua}; } 1; __END__
- Miller

Replies are listed 'Best First'.
Re^2: Complex conditional sort
by ikegami (Patriarch) on Mar 09, 2011 at 01:48 UTC
    I'd recommend against using the negation operator to return a positive value for true, not so much because there no guarantee that it will continue to do so, but because the reader probably won't know it returns positive for true. (Especially since some languages use a negative value for true!) It's not like $x && 1 or $x ?1:0 would be a burden to use instead of !!$x.

      Fair enough ikegami.

      I like using the !!$test construct to force a boolean to 1 or (''/0). However, if that isn't clear enough, I'd probably choose using the >0 test.

      sort {$products{Cat}{Pro}{$b}{Qua}>0 <=> $products{Cat}{Pro}{$a}{Q +ua}>0 || $a <=> $b}

      I suppose it still requires some level of understanding of the possible return values of conditionals, but that's one of the most important things to be familiar with when dealing with sorting in my opinion. Especially the <=> and cmp operators.

        but that's one of the most important things to be familiar with when dealing with sorting in my opinion. Especially the <=> and cmp operators.

        Only those. Again, Perl makes no guarantee as to what the others return. It's definitely not important to be familiar with what they return as they can return anything true and anything false. In fact, they don't always return the values you think they return.

        I'd probably choose using the >0 test.

        Same exact problem as negation.

Re^2: Complex conditional sort
by Anonymous Monk on Mar 09, 2011 at 01:17 UTC
    Thank you everyone for your suggestions. I tried most of your examples, although a couple didn't work as I desired (unless I implemented them incorrectly). I have used Limbic~Region's second example, its tidy and functions well:
    foreach my $col ( sort { my $aa = $products{$a}{Q} > 0 ? "A$a" : "B$a"; my $bb = $products{$b}{Q} > 0 ? "A$b" : "B$b"; ($aa <=> $bb || $aa cmp $bb) } keys %products ) {
    Thanks again, Chris

      Why did you reintroduce ($aa <=> $bb || $aa cmp $bb)? First, it goes against what you said ("each group must then be sorted in alphabetical order"). Secondly, it implies that you have colour names that start with numbers, and that doesn't make much sense. Maybe you are doing a cheap "natural sort", but you'd would at least need a no warnings; to accompany it.

      Update: Expanded to clarify.

        The code is part of a larger eCommerce CGI script. While its live, warnings are turned off, therefore it wasn't an issue even if its bad practice. The sort must be able to handle numbers alone, or letters alone, or a combination of both (which I didn't explain properly). That was just my natural attempt to do that. I haven't tried yet, but i'm assuming cmp alone might be able to handle it. Chris