0

目标:从数组中删除特定值

我写了一个脚本,它运行良好,但我对我编写它的方式不满意。所以我很想知道有没有更好的方法来写它。请考虑以下用例:

我有一个嵌套的哈希/哈希/数组...如下所示。我需要删除local名称中包含的任何数组值:

#!/usr/bin/perl -w
use strict;
use Data::Dumper;

my $hash = { esx1 => 
                    { cluster   => "clu1",
                      fd        => "fd1",
                      ds        => [ 
                                        'ds1',
                                        'ds2',
                                        'localds',
                                    ],  
                    },  
            esx2 => 
                    { cluster   => "clu2",
                      fd        => "fd2",
                      ds        => [ 
                                        'ds3',
                                        'ds4',
                                        'dslocal',
                                    ],  
                    },  
            };  


foreach my $a ( keys %$hash )
{
    foreach ( 0..$#{ $hash->{$a}->{ds} } ) 
    {   
        delete $hash->{$a}->{ds}->[$_] if $hash->{$a}->{ds}->[$_] =~ /local/i;
        @{ $hash->{$a}->{ds} } = grep defined, @{ $hash->{$a}->{ds} };
    }   
}

print Dumper ($hash);

所以脚本会删除“ localds”和“ dslocal”并保持其他所有内容不变。

问题

  1. 有没有更简洁的方法来编写foreach ( 0..$#{$hash->{$a}->{ds} } )循环
  2. 如果我不写grep上面的行,则结果数组的值包含local已删除但被替换为undef. 为什么会这样。

谢谢。

4

5 回答 5

5

delete用于哈希中的元素。它碰巧通过一个怪癖的实现在数组上工作,但不应该依赖它。

对于数组,您想使用拼接。

splice @{ $ref->{to}->{array} }, $index, 1, ();

$index这将替换以空列表开头的 1 元素子()列表。

于 2012-10-31T15:06:41.747 回答
2

为什么首先遍历数组并删除元素,然后查找“未删除”节点(旁注 - 这grep应该在循环之外)?您可以从一开始就寻找好的节点!将整个循环替换为:

foreach my $a ( keys %$hash )
{
    @{ $hash->{$a}->{ds} } = grep { !/local/i } @{ $hash->{$a}->{ds} };
}
于 2012-10-31T15:08:14.320 回答
1

不是更整洁,但是:

foreach my $a ( keys %$hash )
{
    my $temp;
    foreach ( @{ $hash->{$a}->{ds} } ) 
    {   
        push(@$temp, $_) unless $_ =~ /local/i;
    }
    $hash->{$a}->{ds} = $temp;
}

删除不会改变数组结构,它只是改变数组内容。因此,在您的方法中,您需要 grep 查找已定义的条目以创建所需结构的新数组,然后覆盖旧数组。

编辑:这在perldoc page for delete上有更好的解释

delete() 也可以用于数组和数组切片,但它的行为不那么直接。尽管 exists() 会为已删除的条目返回 false,但删除数组元素永远不会更改现有值的索引;为此使用 shift() 或 splice()。但是,如果所有被删除的元素都落在数组的末尾,则数组的大小将缩小到仍然为存在()测试为真的最高元素的位置,如果没有,则缩小为 0。

编辑:

正如 mob splice 所指出的,它将做你想做的事:

foreach my $a ( keys %$hash )
{
    for(0..$#{ $hash->{$a}->{ds} } ) 
    {   
        splice( @{ $hash->{$a}->{ds} }, $_, 1 ) if $hash->{$a}->{ds}->[ $_ ] =~ /local/i;
    }
}
于 2012-10-31T14:52:59.960 回答
1
for my $h (values %$hash){
    $h->{ds} = [ grep { $_ !~ /local/i } @{$h->{ds}} ];
}
于 2012-10-31T15:06:54.620 回答
0

您可以使用 List::MoreUtils qw/first_idx/ 来获取 /local/ 的索引,如下所示

first_idx { $_ =~ /local/i } @{$hash->{$a}->{ds}};

然后用它做你想做的事。

于 2012-10-31T15:00:04.433 回答