Smartelf
Smartelf

Reputation: 879

perl largest scalar value

I have some code that's working, but spitting out a lot of warnings.

    foreach my $item ( sort {$item_rank{$a} <=> $item_rank{$b}} @items{
        ...
    }

My problem is that not every item has a rank, and so my output is littered with warnings. I want to make the items with no rank last. I am thinking of changing the code to as follows:

    foreach my $item ( sort {
                              $item_rank{$a} = 99999 if(!exist $item_rank{$a});
                              $item_rank{$b} = 99999 if(!exist $item_rank{$b});
                              $item_rank{$a} <=> $item_rank{$b}} @items{
        ...
    }

My question is, is there a particular value that I can set it to instead of 99999, although I will never reach 99999 in my current setup, I want my code more robust.

Thanks

Upvotes: 2

Views: 275

Answers (2)

TLP
TLP

Reputation: 67900

Since you have a clear definition of the criteria for unranked records, you might as well just separate the two groups:

my @unranked = grep ! defined($item_rank{$_}), keys %item_rank;
my @ranked   = grep   defined($item_rank{$_}), keys %item_rank;

Then you can sort as per usual, and you can even include the two arrays in the same for loop:

for my $item (sort ( { $item_rank{$a} <=> $item_rank{$b} } @ranked), @unranked) {
    print "$item => ", $item_rank{$item} // "N/A", "\n";
}

This will automatically put all your unranked items last.

Note that you need parens around the arguments to sort to avoid the elements of @unranked becoming part of the arguments for sort. I used the defined-or operator // to check for undefined values in the print statement.

You can even move the sort up to the grep statements:

my @unranked = grep ! defined($item_rank{$_}), keys %item_rank;
my @ranked   = sort { $item_rank{$a} <=> $item_rank{$b} }
                   grep defined($item_rank{$_}), keys %item_rank;

for my $item (@ranked, @unranked) { 
...

Upvotes: 3

DVK
DVK

Reputation: 129481

You can do it one of 2 ways:

  • If you have defined max ceiling, default to it (To simplify the code, I am assuming 0 is not a valid rank)

    $max_ceiling = 99999;
    sort { ($item_rank{$a} || $max_ceiling)
       <=> ($item_rank{$b} || $max_ceiling) } @items
    

    Or, to avoid assigning to the hash:

    # Ideally, %item_rank should be passed as a parameter but meh.
    sub rank4comp { exist $item_rank{$_[0]} ? $item_rank{$_[0]} : 999999; }
    sort { rank4comp($a) <=> rank4comp($b) } @items;
    
  • Better yet, check for undefs explicitly in your expression (remember that sort's code block can be ANY expression, which returns negative, 0 or positive ala "<=>":

    sort { defined $item_rank{$a}
         ? defined $item_rank{$b}
         ? $item_rank{$a} <=> $item_rank{$b} : 1 : -1 } @items;
    

Upvotes: 4

Related Questions