Каков наилучший способ удалить значение из массива в Perl?

массив имеет много данных, и мне нужно удалить два элемента.

Ниже приведен фрагмент кода, который я использую,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;

12 ответов


используйте splice, если вы уже знаете индекс элемента, который хотите удалить.

Grep работает, если вы ищете.

Если вы должны сделать много из них, вы получите гораздо лучшую производительность, если вы храните массив в отсортированном порядке, так как вы можете сделать бинарный поиск, чтобы найти необходимый индекс.

Если это имеет смысл в вашем контексте, вы можете использовать "магическое значение" для удаленных записей, а не удалять их, чтобы сохранить данные перемещение - например, установить удаленные элементы в undef. Естественно, это имеет свои проблемы (если вам нужно знать количество "живых" элементов, вам нужно отслеживать его отдельно и т. д.), Но может стоить проблем в зависимости от вашего приложения.

редактировать на самом деле теперь, когда я смотрю второй раз-не используйте код grep выше. Было бы более эффективно найти индекс элемента, который вы хотите удалить, а затем использовать splice для его удаления (код, который у вас есть накапливает все несоответствующие результаты..)

my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);

это удалит первое вхождение. Удаление всех вхождений очень похоже, за исключением того, что вы захотите получить все индексы за один проход:

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

остальное остается как упражнение для читателя - помните, что массив изменяется по мере его соединения!

Edit2 Джон Сиракузы правильно указал, что у меня была ошибка в моем примере.. исправлено, извините за это.


соединение удалит элемент(ы) массива по индексу. Команда grep, как в вашем примере, для поиска и удаления.


Это то, что вы собираетесь делать много? Если это так, вы можете рассмотреть другую структуру данных. Grep будет искать весь массив каждый раз, и для большого массива может быть довольно дорогостоящим. Если скорость является проблемой, вы можете рассмотреть возможность использования хэша вместо этого.

в вашем примере ключом будет число, а значением будет количество элементов этого числа.


Если вы измените

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

to

my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);

это позволяет избежать проблемы перенумерации массива, сначала удалив элементы из задней части массива. Помещение splice () в цикл foreach очищает @arr. Относительно простой и читаемый...

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}

Я думаю, что ваше решение является самым простым и наиболее ремонтопригодны.

остальные пост-документы сложность превращения тестов на элементы в splice смещения. Таким образом, делая его более полное ответ.

посмотреть круговращение вы должны пройти, чтобы иметь эффективный (т. е. один проход) алгоритм для превращения тестов на элементы списка в индексы. И это совсем не интуитивно.

sub array_remove ( \@& ) { 
    my ( $arr_ref, $test_block ) = @_;
    my $sp_start  = 0;
    my $sp_len    = 0;
    for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
        local $_ = $arr_ref->[$inx];
        next unless $test_block->( $_ );
        if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
            splice( @$arr_ref, $sp_start, $sp_len );
            $inx    = $inx - $sp_len;
            $sp_len = 0;
        }
        $sp_start = $inx if ++$sp_len == 1;
    }
    splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
    return;
}

Я использую:

delete $array[$index];

Perldoc удалить.


удалить все вхождения 'что-то' если в массиве.

на основе ответа SquareCog:

my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
    splice(@arr, $_-$o, 1);
    $o++;
}
print join("\n", @arr);

каждый раз, когда мы удаляем указатель из @arr, следующий правильный индекс для удаления будет $_-current_loop_step.


вы можете использовать группу без захвата и список элементов delim канала для удаления.


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'

лучшее, что я нашел, было сочетание "undef"и " grep":

foreach $index ( @list_of_indexes_to_be_skiped ) {
      undef($array[$index]);
}
@array = grep { defined($_) } @array;

это делает трюк! Федерико!--2-->


вы можете использовать нарезку массива вместо сращивания. Grep для возврата индексов, которые вы хотите сохранить и использовать нарезку:

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];

Если вы знаете индекс массива, вы можете delete () его. Разница между splice() и delete() заключается в том, что delete () не перенумерует оставшиеся элементы массива.


аналогичный код, который я однажды написал, чтобы удалить строки, не начинающиеся с SB.1 из массива строк

my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {  
    unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}