对键包含非字母数字字符的散列进行排序
Sorting a hash where keys contain non-alphanumeric characters
我有如下哈希:
my %hash=( '(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
我想使用键对哈希进行排序,其中键包含方括号。我试过下面的代码,
foreach $key(sort {$b <=> $a} keys %hash)
{
print $key;
}
我得到了以下未按数字排序的内容:
(888 to 891)(2016 to 2019)(293 to 296)(3118 to 3121)(3102 to 3105)(330
to 333)(1999 to 2002)(2126 to 2129)(2138 to 2141)(771 to 774)
我期待一个输出,它按如下数字排序。请建议我实现以下目标的方法:
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
'(293 to 296)'
不是数字(甚至不以数字开头)因此尝试按数字排序没有任何意义。
您可以从中提取第一个数字并对其进行排序。
($a) = ($a =~ /(\d+)/);
($b) = ($b =~ /(\d+)/);
试试这个
在下面的脚本中,我使用模式匹配删除了带有 /r
标志的 ( )
。
它有助于保存来自替换的原始数据。然后它将按数字排序。
my %hash=( '(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
foreach my $i (sort { $a=~s/\(//rg <=> $b=~s/\(//rg } keys %hash)
{
print "$i\n";
}
sort
通过将 $a
和 $b
传递给函数,然后 returning -1
、0
或 +1
.
最简单的 - 按第一个数字排序 - 是这样的:
sort { $a =~ s/.(\d+).*//r <=> $b =~ s/.*(\d+).*//r } keys %hash
这会从每个键中提取第一个数值,进行比较并 return 比较值。
当然,如果您的范围重叠,这将无法按照您想要的方式工作 - 您必须变得更复杂一些 - 如果您有:
100 到 200
150 至 180
120 到 205
应该如何排序?不管怎样——你写了一个 'works' 在 $a
和 $b
上的子程序并执行比较。这里一个有用的技巧是 'standard' 排序运算符 - <=>
和 cmp
- return 为零,因此可以使用 ||
.[=25= 简化]
所以:
sub compare_numbers {
my @a = $a =~ m/(\d+)/g;
my @b = $b =~ m/(\d+)/g;
return ( $a[0] <=> $b[0]
|| $a[1] <=> $b[1] )
}
如果第一次比较为零,则计算第二次。
或者你可以计算中间值:
sub compare_numbers {
my @a = $a =~ m/(\d+)/g;
my @b = $b =~ m/(\d+)/g;
return ( ($a[1] - $a[0] / 2 + $a[0]) <=> ($b[1] - $b[0] / 2 + $b[0])
}
您将以与上述类似的方式使用其中任何一个:
sort compare_numbers keys %hash
您可以使用 "naturally" 对值进行排序的 CPAN 模块之一(例如, 您可以使用 Sort::Naturally
)。
虽然这会隐藏正在发生的事情。所以出于教育目的,我喜欢 @Sobrique
, @Borodin
and @Quentin
的解释。
use Sort::Naturally;
my @nsorted ;
@nsorted = nsort ( <DATA> ) ;
print @nsorted;
__DATA__
(293 to 296)
(3118 to 3121)
(330 to 333)
(2126 to 2129)
(1999 to 2002)
(2138 to 2141)
(771 to 774)
(2016 to 2019)
(888 to 891)
(3102 to 3105)
输出:
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
问题是 (293 to 296)
这样的字符串没有数值。如果您按照应有的方式设置了 use warnings 'all'
,您会看到多个警告,例如
Argument "(293 to 296)" isn't numeric in sort
并且每个键的计算结果均为零,因此就 sort
而言,它们都相等
因此您必须从每个值中提取一个数字以用于数字排序。我会抓住每个范围的下限并按其排序。
use strict;
use warnings 'all';
use feature 'say';
my %hash = (
'(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
my @keys = sort {
my ($aa, $bb) = map /(\d+)/, $a, $b;
$aa <=> $bb;
} keys %hash;
say for @keys;
输出
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
使用 List::MoreUtils
or List::UtilsBy
中的 nsort_by
函数可以使这变得更加简洁
use List::MoreUtils 'nsort_by';
say for nsort_by { /(\d+)/ and } keys %hash;
这段代码的输出与上面的相同
我有如下哈希:
my %hash=( '(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
我想使用键对哈希进行排序,其中键包含方括号。我试过下面的代码,
foreach $key(sort {$b <=> $a} keys %hash)
{
print $key;
}
我得到了以下未按数字排序的内容:
(888 to 891)(2016 to 2019)(293 to 296)(3118 to 3121)(3102 to 3105)(330 to 333)(1999 to 2002)(2126 to 2129)(2138 to 2141)(771 to 774)
我期待一个输出,它按如下数字排序。请建议我实现以下目标的方法:
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
'(293 to 296)'
不是数字(甚至不以数字开头)因此尝试按数字排序没有任何意义。
您可以从中提取第一个数字并对其进行排序。
($a) = ($a =~ /(\d+)/);
($b) = ($b =~ /(\d+)/);
试试这个
在下面的脚本中,我使用模式匹配删除了带有 /r
标志的 ( )
。
它有助于保存来自替换的原始数据。然后它将按数字排序。
my %hash=( '(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
foreach my $i (sort { $a=~s/\(//rg <=> $b=~s/\(//rg } keys %hash)
{
print "$i\n";
}
sort
通过将 $a
和 $b
传递给函数,然后 returning -1
、0
或 +1
.
最简单的 - 按第一个数字排序 - 是这样的:
sort { $a =~ s/.(\d+).*//r <=> $b =~ s/.*(\d+).*//r } keys %hash
这会从每个键中提取第一个数值,进行比较并 return 比较值。
当然,如果您的范围重叠,这将无法按照您想要的方式工作 - 您必须变得更复杂一些 - 如果您有:
100 到 200 150 至 180 120 到 205
应该如何排序?不管怎样——你写了一个 'works' 在 $a
和 $b
上的子程序并执行比较。这里一个有用的技巧是 'standard' 排序运算符 - <=>
和 cmp
- return 为零,因此可以使用 ||
.[=25= 简化]
所以:
sub compare_numbers {
my @a = $a =~ m/(\d+)/g;
my @b = $b =~ m/(\d+)/g;
return ( $a[0] <=> $b[0]
|| $a[1] <=> $b[1] )
}
如果第一次比较为零,则计算第二次。
或者你可以计算中间值:
sub compare_numbers {
my @a = $a =~ m/(\d+)/g;
my @b = $b =~ m/(\d+)/g;
return ( ($a[1] - $a[0] / 2 + $a[0]) <=> ($b[1] - $b[0] / 2 + $b[0])
}
您将以与上述类似的方式使用其中任何一个:
sort compare_numbers keys %hash
您可以使用 "naturally" 对值进行排序的 CPAN 模块之一(例如, 您可以使用 Sort::Naturally
)。
虽然这会隐藏正在发生的事情。所以出于教育目的,我喜欢 @Sobrique
, @Borodin
and @Quentin
的解释。
use Sort::Naturally;
my @nsorted ;
@nsorted = nsort ( <DATA> ) ;
print @nsorted;
__DATA__
(293 to 296)
(3118 to 3121)
(330 to 333)
(2126 to 2129)
(1999 to 2002)
(2138 to 2141)
(771 to 774)
(2016 to 2019)
(888 to 891)
(3102 to 3105)
输出:
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
问题是 (293 to 296)
这样的字符串没有数值。如果您按照应有的方式设置了 use warnings 'all'
,您会看到多个警告,例如
Argument "(293 to 296)" isn't numeric in sort
并且每个键的计算结果均为零,因此就 sort
而言,它们都相等
因此您必须从每个值中提取一个数字以用于数字排序。我会抓住每个范围的下限并按其排序。
use strict;
use warnings 'all';
use feature 'say';
my %hash = (
'(293 to 296)' => 2,
'(3118 to 3121)' => 2,
'(330 to 333)' => 2,
'(2126 to 2129)' => 2,
'(1999 to 2002)' => 2,
'(2138 to 2141)' => 9,
'(771 to 774)' => 4,
'(2016 to 2019)' => 1,
'(888 to 891)' => 5,
'(3102 to 3105)' => 1,
);
my @keys = sort {
my ($aa, $bb) = map /(\d+)/, $a, $b;
$aa <=> $bb;
} keys %hash;
say for @keys;
输出
(293 to 296)
(330 to 333)
(771 to 774)
(888 to 891)
(1999 to 2002)
(2016 to 2019)
(2126 to 2129)
(2138 to 2141)
(3102 to 3105)
(3118 to 3121)
使用 List::MoreUtils
or List::UtilsBy
中的 nsort_by
函数可以使这变得更加简洁
use List::MoreUtils 'nsort_by';
say for nsort_by { /(\d+)/ and } keys %hash;
这段代码的输出与上面的相同