Jaki jest najlepszy sposób na usunięcie wartości z tablicy w Perlu?

Tablica ma dużo danych i muszę usunąć dwa elementy.

Poniżej znajduje się fragment kodu, którego używam,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
 73
Author: Sam, 2008-10-06

12 answers

Użyj splice, jeśli znasz już indeks elementu, który chcesz usunąć.

Grep działa, jeśli szukasz.

Jeśli potrzebujesz wykonać wiele z nich, uzyskasz znacznie lepszą wydajność, jeśli utrzymasz tablicę w porządku posortowanym, ponieważ możesz następnie wykonać wyszukiwanie binarne, aby znaleźć odpowiedni indeks.

Jeśli ma to sens w Twoim kontekście, możesz rozważyć użycie "magicznej wartości" dla usuniętych rekordów, zamiast ich usuwania, aby zapisać ruch danych -- set deleted elementy do undef, na przykład. Oczywiście ma to swoje własne problemy (jeśli musisz znać liczbę "żywych" elementów, musisz śledzić je osobno itp.), ale może być warte zachodu w zależności od aplikacji.

Edit właściwie teraz, gdy zajrzę po raz drugi -- nie używaj powyższego kodu grepa. Lepiej byłoby znaleźć indeks elementu, który chcesz usunąć, a następnie użyć splice, aby go usunąć (kod, który masz, gromadzi wszystkie niepasujące wyniki..)

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

To usunie pierwsze wystąpienie. Usuwanie wszystkich wystąpień jest bardzo podobne, z wyjątkiem tego, że będziesz chciał uzyskać wszystkie indeksy w jednym przejściu:

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

Reszta jest pozostawiona jako ćwiczenie dla czytelnika-pamiętaj, że tablica zmienia się w miarę jej łączenia!

Edit2 John Siracusa poprawnie wskazał, że mam błąd w moim przykładzie.. naprawione, przepraszam za to.

 80
Author: SquareCog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 16:44:23

Splice usunie elementy tablicy według indeksu. Użyj grep, jak w przykładzie, aby wyszukać i usunąć.

 13
Author: spoulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 13:30:44

Czy to jest coś, co zamierzasz robić często? Jeśli tak, możesz rozważyć inną strukturę danych. Grep będzie przeszukiwał całą tablicę za każdym razem, a dla dużej tablicy może to być dość kosztowne. Jeśli prędkość jest problemem, możesz rozważyć użycie skrótu.

W twoim przykładzie kluczem będzie Liczba, a wartością liczba elementów tej liczby.

 8
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 13:54:52

Jeśli zmienisz

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

Do

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

Pozwala to uniknąć problemu zmiany numeracji tablicy, usuwając najpierw elementy z tyłu tablicy. Umieszczenie splice () W pętli foreach czyści @arr. Stosunkowo proste i czytelne...

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}
 5
Author: dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 07:35:15

Myślę, że Twoje rozwiązanie jest najprostsze i najbardziej możliwe do utrzymania.

Reszta postu dokumentuje trudność przekształcania testów na elementach w splice offsety. W ten sposób, czyniąc ją bardziej kompletną odpowiedź.

Spójrz na żyrandole musisz przejść, aby mieć wydajny (tj. jednokrotny) algorytm, który zamienia testy na elementach listy w indeksy. I to wcale nie jest takie intuicyjne.

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;
}
 3
Author: Axeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 06:33:04

Używam:

delete $array[$index];

Perldoc usunąć.

 2
Author: Ariel Monaco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-23 17:13:15

Usuń wszystkie wystąpienia tablicy' something ' if.

Na podstawie odpowiedzi 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);

Za każdym razem, gdy usuwamy Indeks z @arr, następnym poprawnym indeksem do usunięcia będzie $_-current_loop_step.

 2
Author: Tom Lime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-23 17:16:51

Możesz użyć Grupy bez przechwytywania i listy elementów do usunięcia.


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'
 2
Author: Rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 16:29:21

Najlepsze jakie znalazłem było połączenie "undef" i "grep":

foreach $index ( @list_of_indexes_to_be_skiped ) {
      undef($array[$index]);
}
@array = grep { defined($_) } @array;
To wystarczy! Federico
 2
Author: Federico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-19 13:01:51

Możesz użyć krojenia tablicy zamiast splicingu. Grep zwraca indeksy, które chcesz zachować i użyć krojenia:

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
 2
Author: oryan_dunn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-20 19:34:52

Jeśli znasz indeks tablicy, możesz usunąć () . Różnica między splice () I delete() polega na tym, że delete() nie zmienia numeracji pozostałych elementów tablicy.

 0
Author: Powerlord,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-06 16:34:10

Podobny kod, który kiedyś napisałem, aby usunąć ciągi nie zaczynające się od SB.1 z tablicy łańcuchów

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);}
}
 0
Author: BBT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-27 10:02:26