Domanda

I have a hash keys such as:

FastEthernet1
GigabitEthernet1/1
GigabitEthernet1/10
GigabitEthernet1/2
GigabitEthernet1/20

But I have them from 1-48 and I also have 2/1 - 48 etc.

This foreach my $i (sort keys %intconfigs) will print exactly as above.

But what I want is:

FastEthernet1
GigabitEthernet1/1
GigabitEthernet1/2
GigabitEthernet1/10
GigabitEthernet1/20

I tried this foreach my $i (sort { $a <=> $b } keys %intconfigs) but that comes out to:

GigabitEthernet1/2
GigabitEthernet1/10
GigabitEthernet1/20
GigabitEthernet1/1
FastEthernet1

Not sure how to sort this properly. Any help would be appreciated.

È stato utile?

Soluzione

You can break the values into parts so that you can sort each section appropriately, whether by alpha or by numerical comparisons:

my @keys = qw(
    FastEthernet1
    GigabitEthernet1/1
    GigabitEthernet1/10
    GigabitEthernet1/2
    GigabitEthernet1/20
);

my @sorted = sort {
    my ($a_name, $a_num1, $a_num2) = $a =~ m{(.*?)(\d+)(?:/(\d+))?};
    my ($b_name, $b_num1, $b_num2) = $b =~ m{(.*?)(\d+)(?:/(\d+))?};
    $a_name cmp $b_name or $a_num1 <=> $b_num1 or $a_num2 <=> $b_num2;
} @keys;

print "$_\n" for @sorted;

Or using some more advanced regex techniques:

my @sorted = sort {
    local ( $a, $b ) = map { m{(?<name>\D+) (?<num1>\d+) (?: / (?<num2>\d+))?}x ? {%+} : die "Can't parse: $_" } ( $a, $b );
    $a->{name} cmp $b->{name} or $a->{num1} <=> $b->{num1} or $a->{num2} <=> $b->{num2}
} @keys;

Or use the module Sort::Key::Natural, to automatically sort numerical parts numerically:

use Sort::Key::Natural qw(natsort);

my @sorted = natsort @keys;

print "$_\n" for @sorted;

Both methods output:

FastEthernet1
GigabitEthernet1/1
GigabitEthernet1/2
GigabitEthernet1/10
GigabitEthernet1/20

Altri suggerimenti

Another easy one that would work in your case would be to string sort on a variation of the string, formed by 0-padding all of the embedded numbers into a long series of digits, such that they string sort correctly.

use List::UtilsBy qw( sort_by );

my @ifaces = sort_by { s/(\d+)/sprintf "%09d", $1/eg; $_ } qw(
  FastEthernet1
  GigabitEthernet1/1
  GigabitEthernet1/10
  GigabitEthernet1/2
  GigabitEthernet1/20
);

say for @ifaces

Indeed prints them in the required order.

It's not quite a universal solution, as in this case it will break for strings containing digit sequences longer than 9 digits, or floating-point numbers.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top