Вопрос

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.

Это было полезно?

Решение

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

Другие советы

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top