【问题标题】:Is it possible to iterate through a hash in sorted order using the while(my($key, $value) ... ) {} method?是否可以使用 while(my($key, $value) ... ) {} 方法按排序顺序遍历哈希?
【发布时间】:2025-11-21 05:35:04
【问题描述】:

对于这种格式的哈希:

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 格式按照每个项目的哈希中的“order”键指定的顺序遍历它们?

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

【问题讨论】:

  • Tie::IxHash 模块可能值得一看,但您必须更改代码以按照与“订单”键匹配的顺序插入哈希,或调用 Tie::IxHash @ 987654326@ 方法有时可以将所有内容按正确的顺序排列。

标签: perl perl-data-structures


【解决方案1】:

你可以这样做:

foreach my $key (sort keys %{$itemHash}) {
    print "$key : " . $itemHash->{$key}{name} . "\n";
}

【讨论】:

  • 谢谢,比其他答案好多了!
【解决方案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";
}

【讨论】:

    【解决方案3】:

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

    要完成您的任务,您必须按 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});
      };
    }
    

    创建一个回调,它是 each 的一个插件。

    我们可以这样做:

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

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

    【讨论】:

    • 是的,“排序哈希”可能不是描述它的好方法,但我试图在标题中简洁。谢谢!
    【解决方案4】:

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

    另一种方法是即时对它们进行排序。我不确定你会认为它更清洁。比如:

    for my $key ( sort { $itemHash->{$a}{order} <=> $itemhash->{$b}{order} } keys %$itemHash ) {
      print "$key is $itemHash->{$key}{name}";
    }
    

    【讨论】:

    • 是的。我知道这种方法。我想知道是否有短(呃)手的方式来做同样的while循环
    最近更新 更多