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



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



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



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

12 ответов:

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

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

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

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

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

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

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

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

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

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

до

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

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

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

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

остальная часть post документирует сложность превращения тестов на элементах в 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.

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


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;

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

вы можете использовать массив нарезки вместо сплайсинга. 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);}
}

Comments

    Ничего не найдено.