Подтвердить что ты не робот

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

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

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

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

Ответ 1

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

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 Джон Сиракуза правильно указал, что у меня была ошибка в моем примере.. исправлено, извините.

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

to

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

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

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

Ответ 5

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

Остальная часть сообщения документирует трудность перевода тестов на элементы в смещения 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;
}

Ответ 6

Удалить все вхождения "something" if array.

Основываясь на ответе 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.

Ответ 7

Я использую:

delete $array[$index];

Perldoc удалить.

Ответ 8

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


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

Ответ 9

Лучшее, что я нашел, это комбинация "undef" и "grep":

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

Это трюк! Federico

Ответ 10

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

Ответ 11

Аналогичный код, который я однажды написал, чтобы удалить строки, не начинающиеся с 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);}
}

Ответ 12

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

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