完全删除数组元素

时间:2012-10-31 14:41:06

标签: perl

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

我已经写了一个脚本并且它工作正常,但我写的方式并不高兴。所以我很想知道是否有更好的方法来编写它。请考虑以下用例:

我有一个嵌套的哈希/哈希/数组......如下所示。我需要删除名称中包含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替换。为什么会这样呢。
  3. 感谢。

5 个答案:

答案 0 :(得分:5)

delete用于散列中的元素。它恰好通过一个实现的怪癖在数组上工作,但不应该依赖它。

对于数组,您希望使用splice。

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

这将替换从$index开始的1元素子列表,其中()为空列表。

答案 1 :(得分:2)

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

foreach my $a ( keys %$hash )
{
    @{ $hash->{$a}->{ds} } = grep { !/local/i } @{ $hash->{$a}->{ds} };
}

答案 2 :(得分: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()。但是,如果所有已删除的元素都落在数组的末尾,则数组的大小会缩小到仍然为exists()测试为true的最高元素的位置,如果没有,则为0。

编辑:

正如mob splice所指出的,你会做你想做的事情:

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

答案 3 :(得分:1)

for my $h (values %$hash){
    $h->{ds} = [ grep { $_ !~ /local/i } @{$h->{ds}} ];
}

答案 4 :(得分:0)

您可以使用List :: MoreUtils qw / first_idx /来获取/ local / like索引

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

然后用你做你想做的事。