【问题标题】:Perl grep command for nested hash instead of using loopsPerl grep 命令用于嵌套哈希而不是使用循环
【发布时间】:2020-10-08 05:35:01
【问题描述】:

我有以下哈希结构。

$VAR1 = {
    'USA' => {
        'Alabama' => {
            'ISO3'    => 'ISO3:3166-2:US',
            'ISO2'    => 'ISO2:4166-23:US',
            'UNI'     => 'UNIABR-A',
            'UNDP'    => 'UNDP-ZXC-1',
            'FAOSTAT' => 'STAT20.98',
            'GAUL'    => 'UL-SD-20/40'
        },
        'Washington' => {
            'ISO3'    => 'ISO3:40-166-2:US',
            'ISO2'    => 'ISO2:30-23:US',
            'UNI'     => 'UNIISO-B',
            'UNDP'    => 'UNDP-YXC-2',
            'FAOSTAT' => 'STAT30.98.78',
            'GAUL'    => 'UL-SD-30/60'
        }
    }
};

我想要实现的是遍历上述哈希并获取哈希“ISO2:4166-23:US”中值的州名和国家/地区名称。我试图做的是:

我可以使用以下代码获得所需的输出。

my $find = "ISO2:4166-23:US";
my $statename;

while ( my ($country, $states) = each (%$VAR1) ) {
    while (my ($states, $otherkeys) = each (%$states) ) {
        while (my ($otherkeys, $value) = each %$otherkeys) {
            $statename = $states if ($value eq $find);
        }
    }
}

print "State name for value [$find] is :: $statename \n"; ### Output : Alabama

有什么办法可以得到-

如果$value 等于ISO2:4166-23:US,则获取top-level key“USA”和second-level key“Alabama”。我所知道的是我需要获取输出的哈希值,与我的搜索值对应的键无关紧要。

使用来自上述哈希的单行 grep 命令?

任何指向正确方向的指针都会很有用。谢谢。

【问题讨论】:

  • 你为什么要问这个问题?感觉有点像 XY 问题。使用循环没有任何问题。
  • 这将需要一系列可能比使用 while 循环更易读的映射和 grep。而且你什么也得不到,因为 grep 和 map 也是循环。

标签: perl


【解决方案1】:

您的变量命名不当。重用$states 作为州名?哎哟。

my $find = "ISO2:4166-23:US";

my $found_state_name;
while ( my ($country_name, $states) = each(%$VAR1) ) {
    while (my ($state_name, $state) = each(%$states) ) {
        while ( my ($key, $value) = each(%$state) ) {
            if ($value eq $find) {
                $found_state_name = $state_name;
            }
        }
    }
}

现在,最好在找到结果后立即停止搜索。我们不能在仍然使用each 的情况下这样做(因为稍后会在这些哈希上搞砸keys/values/each)。

my $find = "ISO2:4166-23:US";

my $found_state_name;
FIND:
for my $country_name (keys(%$VAR1)) {
    my $country = $VAR1->{$country_name};
    for my $state_name (keys(%$country)) {
        my $state = $country->{$state_name};
        for my $key (keys(%$state)) {
            if ($state->{$key} eq $find) {
                $found_state_name = $state_name;
                last FIND;
            }
        }
    }
}

我们从不使用$country_name$key,除非是为了获取价值。

my $find = "ISO2:4166-23:US";

my $found_state_name;
FIND:
for my $states (values(%$VAR1)) {
    for my $state_name (keys(%$states)) {
        my $state = $country->{$state_name};
        for my $value (values(%$state)) {
            if ($value eq $find) {
                $found_state_name = $state_name;
                last FIND;
            }
        }
    }
}

如果您知道要查找 ISO2 值,则可以简化为以下内容:

my $find = "ISO2:4166-23:US";

my $found_state_name;
FIND:
for my $states (values(%$VAR1)) {
    for my $state_name (keys(%$states)) {
        my $state = $states->{$state_name};
        if ($state->{ISO2} eq $find) {
            $found_state_name = $state_name;
            last FIND;
        }
    }
}

你想使用grep,嗯?由于您需要一个州名,因此我们需要 grep 一个州名列表。

my @state_names = ...;

my ($found_state_name) =
   grep { ... }
      @state_names;

我们可以使用获取状态名称列表

my @state_names =
   map { keys(%$_) }
      values(%$VAR1);

但这还不足以执行检查。 (现在,我假设只需要检查 ISO2 属性。)

my @state_names =
   map { keys(%$_) }
      values(%$VAR1);

my ($found_state_name) =
   grep { $VAR1->{???}{$_}{ISO2} eq $find }
      @state_names;

有两种解决方案。您可以使用国家/地区对。

my @country_state_name_pairs =;
   map {
      my $country_name = $_;
      map { [ $country_name, $_ ] }
         keys(%{ $VAR1->{$country_name} )
   }
      keys(%$VAR1);

my ($found_state_name) =
   map { $_->[1] }
      grep {
         my ($country_name, $state_name) = @$_;
         $VAR1->{$country_name}{$state_name}{ISO2} eq $find
      }
         @country_state_name_pairs;

或者您可以创建一个平面列表并进行搜索。

my @states_with_name = 
   map { [ $_, $VAR1->{$_} ] }
      values(%$VAR1);

my ($found_state_name) =
   map { $_->[0] }
      grep { $_->[1]{ISO2} eq $find }
         @states_with_name;

注意阻止我们合并这两个语句。

my ($found_state_name) =
   map { $_->[0] }                      # Get the state name.
      grep { $_->[1]{ISO2} eq $find }   # Filter out undesireable states.
         map { [ $_, $VAR1->{$_} ] }    # $state_name => [ $state_name, $state ]
            values(%$VAR1);             # Get the countries.

最后一个还不错!


最后,有两种方法可以修改上面的每一个来搜索所有字段,而不仅仅是ISO2)。 (我将只对上述两种解决方案中的后者进行修改。)

my ($found_state_name) =
   map { $_->[0] }                      # Get the state name.
      grep {                            # Filter out undesireable states.
         grep { $_ eq $find }           # Filter out undesireable properties of the state.
            values(%{ $_->[1] })        # Get the state's property values.
      }
         map { [ $_, $VAR1->{$_} ] }    # $state_name => [ $state_name, $state ]
            values(%$VAR1);             # Get the countries.

my ($found_state_name) =
   map { $_->[0] }                        # Get the state name.
      grep { $_->[1] eq $find }           # Filter out undesireable states.
         map {                            # $state_name => multiple [ $state_name, $value ]
            my $state_name = $_;
            map { [ $state_name, $_ ] }   # value => [ $state_name, $value ]
               values(%{ $VAR1->{$_} )    # Get the state's property values.
         }
            values(%$VAR1);               # Get the countries.

这些是不可读的。最好避免使用它们。


最后,如果您要根据ISO2 执行多次搜索,最好按照ISO2 组织数据。

my %by_iso2 = (
   'ISO2:4166-23:US' => {
      country_name => 'USA',
      state_name   => 'Alabama',
      ISO2         => 'ISO2:4166-23:US',
      ISO3         => 'ISO3:3166-2:US',
      ...
   },
   'ISO2:4166-23:US' => {
      country_name => 'USA',
      state_name   => 'Washington',
      ISO2         => 'ISO2:30-23:US',
      ISO3         => 'ISO3:40-166-2:US',
      ...
   },
   ...
);

【讨论】:

  • 感谢您的宝贵时间,并提供建议的方法的解释,并纠正我采取的方法中的错误。
【解决方案2】:

没有。哈希是单向的,因此您必须遍历所有值才能找到您要搜索的值。

听起来您真正想要的是某种数据库,而哈希对于您的工作来说是错误的工具。

您可以使用 ISO2 作为键,例如

$VAR1 = { 
    "ISO2:4166-23:US" => { Country => 'USA', State => 'Alabama' },
    "ISO2:4166-23:US" => { Country => 'USA', State => 'Washington' }
}

如果您打算经常进行这些查找,那么这可能是值得的。这样做也可以自动化。使用类似的循环构建一个新的哈希值以用作查找。

在速度方面,循环遍历所有哈希键没有任何问题。直接查找和循环之间的区别可以忽略不计,除非你有一个真正巨大的散列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2020-12-09
    • 2019-06-01
    • 1970-01-01
    • 2014-11-14
    • 2018-12-17
    相关资源
    最近更新 更多