4

对于这种格式的哈希:

my $itemHash = {
    tag1 => {
        name => "Item 1",
        order => 1,
        enabled => 1,
    },
    tag2 => {
        name => "Item 2",
        order => 2,
        enabled => 0,
    },
    tag3 => {
        name => "Item 3",
        order => 3,
        enabled => 1,
    },
    ...
}

我有这段代码可以正确地遍历哈希:

keys %$itemHash; # Resets the iterator
while(my($tag, $item) = each %$itemHash) {
    print "$tag is $item->{'name'}"
}

但是,这些项目的迭代顺序似乎非常随机。是否可以使用相同的while格式按照每个项目的哈希中的“订单”键指定的顺序遍历它们?

(我知道我可以先对键进行排序,然后对它进行 foreach 循环。只是想看看是否有更清洁的方法来做到这一点。)

4

4 回答 4

11

您可以执行以下操作:

foreach my $key (sort keys %{$itemHash}) {
    print "$key : " . $itemHash->{$key}{name} . "\n";
}
于 2012-10-12T14:05:01.970 回答
8

“有序散列”的概念是错误的。虽然数组是元素的有序列表,因此可以通过索引访问,但哈希是键值对的(无序)集合,其中键是一个集合。

要完成您的任务,您必须按order属性对键进行排序:

my @sorted = sort {$hash{$a}{order} <=> $hash{$b}{order}} keys %$itemHash;

然后,您可以通过以下方式创建键值对map

my @sortedpairs = map {$_ => $itemHash->{$_}} @sorted;

我们可以把它包装成一个子:

sub ridiculousEach {
  my %hash = @_;
  return map
      {$_ => $hash{$_}}
        sort
          {$hash{$a}{order} <=> $hash{$b}{order}}
             keys %hash;
}

获得一个大小均匀的键值元素列表,以及

sub giveIterator {
  my %hash = @_;
  my @sorted = sort {$hash{$a}{order} <=> $hash{$b}{order}} keys %hash;
  return sub {
     my $key = shift @sorted;
     return ($key => $hash{$key});
  };
}

创建一个回调,该回调是每个的插入。

然后我们可以这样做:

my $iterator = giveIterator(%$itemHash);
while (my ($tag, $item) = $iterator->()) {
  ...;
}

这种方法有一个严重的缺点:each一次只使用两个元素,因此在恒定内存中运行。该解决方案必须读取整个哈希并存储所有键的数组。小散列不明显,这对于大量元素来说很重要。

于 2012-09-25T23:57:54.190 回答
5

哈希中的键的顺序是未定义的。所以你需要对键进行排序。正如您所说,一种方法是拉出密钥并对其进行排序,然后遍历密钥。

另一种方法是即时对它们进行分类。我不确定你会认为它更清洁。就像是:

for my $key ( sort { $itemHash->{$a}{order} <=> $itemhash->{$b}{order} } keys %$itemHash ) {
  print "$key is $itemHash->{$key}{name}";
}
于 2012-09-25T23:55:45.153 回答
2

这样会干净很多。我们必须使用 cmp 对字符串进行排序。

my $itemHash = {
    tag1 => {
        name => "Item 1",
        order => 1,
        enabled => 1,
    },
    tag2 => {
        name => "Item 2",
        order => 2,
        enabled => 0,
    },
    tag3 => {
        name => "Item 3",
        order => 3,
        enabled => 1,
    }
};

foreach((sort{$a cmp $b}(keys(%$itemHash)))){
print "$_ is $itemHash->{$_}->{'name'}\n";
}
于 2012-09-27T08:29:16.223 回答