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

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

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

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
74 голоса | спросил user21246 6 +04002008-10-06T17:29:55+04:00312008bEurope/MoscowMon, 06 Oct 2008 17:29:55 +0400 2008, 17:29:55

12 ответов


0

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

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

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

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

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

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

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

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

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

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

ответил SquareCog 6 +04002008-10-06T17:37:05+04:00312008bEurope/MoscowMon, 06 Oct 2008 17:37:05 +0400 2008, 17:37:05
0

splice удалит элементы массива по индексу. Используйте grep, как в вашем примере, для поиска и удаления.

ответил spoulson 6 +04002008-10-06T17:30:44+04:00312008bEurope/MoscowMon, 06 Oct 2008 17:30:44 +0400 2008, 17:30:44
0

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

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

ответил tvanfosson 6 +04002008-10-06T17:47:56+04:00312008bEurope/MoscowMon, 06 Oct 2008 17:47:56 +0400 2008, 17:47:56
0

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

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

до

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

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

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}
ответил dean 7 Jam1000000amMon, 07 Jan 2013 11:35:15 +040013 2013, 11:35:15
0

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

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

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

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;
}
ответил dean 7 Jam1000000amMon, 07 Jan 2013 11:35:15 +040013 2013, 11:35:15
0

Я использую:

delete $array[$index];

Perldoc delete .

ответил Ariel Monaco 8 FebruaryEurope/MoscowbFri, 08 Feb 2013 09:41:21 +0400000000amFri, 08 Feb 2013 09:41:21 +040013 2013, 09:41:21
0

Удалить все вхождения «что-то», если массив.

На основании ответов 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

ответил Tom Lime 10 PM00000080000004431 2012, 20:50:44
0

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


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'
ответил Rich 11 PMpFri, 11 Apr 2014 20:29:21 +040029Friday 2014, 20:29:21
0

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

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

Это делает трюк! Federico

ответил Federico 19 PMpTue, 19 Apr 2016 15:52:59 +030052Tuesday 2016, 15:52:59
0

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

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
ответил oryan_dunn 20 12017vEurope/Moscow11bEurope/MoscowMon, 20 Nov 2017 22:34:52 +0300 2017, 22:34:52
0

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

ответил Powerlord 6 +04002008-10-06T17:41:38+04:00312008bEurope/MoscowMon, 06 Oct 2008 17:41:38 +0400 2008, 17:41:38
0

Подобный код, который я однажды написал, чтобы удалить строки, не начинающиеся с 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);}
}
ответил BBT 13 MarpmWed, 13 Mar 2013 13:13:52 +04002013-03-13T13:13:52+04:0001 2013, 13:13:52

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132