Bagaimana cara menghapus item duplikat dari array di Perl?


156

Saya memiliki array di Perl:

my @my_array = ("one","two","three","two","three");

Bagaimana cara menghapus duplikat dari array?

Jawaban:


168

Anda dapat melakukan sesuatu seperti ini seperti yang ditunjukkan dalam perlfaq4 :

sub uniq {
    my %seen;
    grep !$seen{$_}++, @_;
}

my @array = qw(one two three two three);
my @filtered = uniq(@array);

print "@filtered\n";

Output:

one two three

Jika Anda ingin menggunakan modul, coba uniqfungsi dariList::MoreUtils


28
tolong jangan gunakan $ a atau $ b dalam contoh karena mereka adalah jenis sihir global ()
szabgab

2
Ini myleksikal dalam lingkup ini, jadi tidak masalah. Karena itu, mungkin nama variabel yang lebih deskriptif dapat dipilih.
ephemient

2
@ephemient ya, tetapi jika Anda menambahkan penyortiran dalam fungsi ini maka akan truf $::adan $::b, bukan?
vol7ron

5
@BrianVandenberg Selamat datang di dunia 1987 - ketika ini dibuat - dan hampir 100% kata kunci backbull untuk perl - sehingga tidak dapat dihilangkan.
szabgab

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }adalah implementasi yang lebih baik karena menjaga ketertiban tanpa biaya. Atau bahkan lebih baik, gunakan yang dari Daftar :: MoreUtils.
ikegami

120

Dokumentasi Perl dilengkapi dengan koleksi FAQ yang bagus. Pertanyaan Anda sering diajukan:

% perldoc -q duplicate

Jawaban, salin dan tempel dari output dari perintah di atas, muncul di bawah ini:

Ditemukan di /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 Bagaimana saya bisa menghapus elemen duplikat dari daftar atau array?
   (disumbangkan oleh brian d foy)

   Gunakan hash. Ketika Anda berpikir kata "unik" atau "digandakan", pikirkan
   "kunci hash".

   Jika Anda tidak peduli dengan urutan elemen, Anda bisa saja
   buat hash lalu ekstrak kuncinya. Tidak penting bagaimana Anda
   buat hash itu: cukup gunakan "kunci" untuk mendapatkan elemen unik.

       % hash saya = map {$ _, 1} @array;
       # atau potongan hash: @hash {@array} = ();
       # atau foreach: $ hash {$ _} = 1 foreach (@array);

       @unique = kunci saya% hash;

   Jika Anda ingin menggunakan modul, coba fungsi "uniq" dari
   "Daftar :: MoreUtils". Dalam konteks daftar itu mengembalikan elemen unik,
   mempertahankan pesanan mereka dalam daftar. Dalam konteks skalar, ia mengembalikan
   sejumlah elemen unik.

       gunakan Daftar :: MoreUtils qw (uniq);

       myunik = uniq (1, 2, 3, 4, 4, 5, 6, 5, 7); # 1,2,3,4,5,6,7
       my $ unique = uniq (1, 2, 3, 4, 4, 5, 6, 5, 7); # 7

   Anda juga dapat menelusuri setiap elemen dan melewati elemen yang telah Anda lihat
   sebelum. Gunakan hash untuk melacak. Pertama kali loop melihat sebuah
   elemen, elemen itu tidak memiliki kunci di% Dilihat. Pernyataan "berikutnya" dibuat
   kunci dan segera menggunakan nilainya, yaitu "undef", jadi loop
   melanjutkan ke "push" dan menambah nilai untuk kunci itu. Selanjutnya
   kali loop melihat elemen yang sama, kuncinya ada di hash dan
   nilai untuk kunci itu benar (karena bukan 0 atau "undef"), jadi
   selanjutnya melewatkan iterasi dan loop ke elemen berikutnya.

       @unique saya = ();
       % saya terlihat = ();

       foreach my $ elem (@array)
       {
         selanjutnya jika $ seen {$ elem} ++;
         push @unique, $ elem;
       }

   Anda dapat menulis ini lebih singkat menggunakan grep, yang melakukan hal yang sama
   benda.

       % saya terlihat = ();
       @unique saya = grep {! $ seen {$ _} ++} @array;


17
John iz di mah anzers mencuri rep mah!
brian d foy

5
Saya pikir Anda harus mendapatkan poin bonus untuk benar-benar mencari pertanyaan.
Brad Gilbert

2
Saya suka jawaban terbaiknya adalah 95% copy-paste dan 3 kalimat OC. Agar sangat jelas, ini adalah jawaban terbaik; Saya hanya menemukan fakta itu lucu.
Parthian Shot

70

Pasang Daftar :: MoreUtils dari CPAN

Kemudian dalam kode Anda:

use strict;
use warnings;
use List::MoreUtils qw(uniq);

my @dup_list = qw(1 1 1 2 3 4 4);

my @uniq_list = uniq(@dup_list);

4
Fakta bahwa List :: MoreUtils tidak dibundel dengan perl agak merusak portabilitas proyek yang menggunakannya :( (I untuk satu tidak akan)
yPhil

3
@Ranguard: @dup_listharus ada di dalam uniqpanggilan, bukan@dups
incutonez

@yassinphilip CPAN adalah salah satu hal yang membuat Perl sama kuat dan hebatnya. Jika Anda menulis proyek Anda hanya berdasarkan modul inti, Anda meletakkan batas yang sangat besar pada kode Anda, bersama dengan kode yang mungkin sekali dituangkan yang mencoba melakukan apa yang beberapa modul lakukan jauh lebih baik hanya untuk menghindari menggunakannya. Juga, menggunakan modul inti tidak menjamin apa pun, karena versi Perl yang berbeda dapat menambah atau menghapus modul inti dari distribusi, sehingga portabilitas masih tergantung pada itu.
Francisco Zarabozo

24

Cara biasa saya melakukan ini adalah:

my %unique = ();
foreach my $item (@myarray)
{
    $unique{$item} ++;
}
my @myuniquearray = keys %unique;

Jika Anda menggunakan hash dan menambahkan item ke hash. Anda juga memiliki bonus untuk mengetahui berapa kali setiap item muncul dalam daftar.


2
Ini memiliki kelemahan dari tidak mempertahankan pesanan asli, jika Anda membutuhkannya.
Nathan Fellman

Lebih baik menggunakan irisan daripada foreachloop:@unique{@myarray}=()
Onlyjob

8

Variabel @array adalah daftar dengan elemen duplikat

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;

7

Dapat dilakukan dengan Perl satu liner sederhana.

my @in=qw(1 3 4  6 2 4  3 2 6  3 2 3 4 4 3 2 5 5 32 3); #Sample data 
my @out=keys %{{ map{$_=>1}@in}}; # Perform PFM
print join ' ', sort{$a<=>$b} @out;# Print data back out sorted and in order.

Blok PFM melakukan ini:

Data dalam @in dimasukkan ke dalam MAP. MAP membangun hash anonim. Kunci diekstraksi dari hash dan diumpankan ke @out


4

Yang terakhir itu cukup bagus. Saya hanya akan men-tweak sedikit:

my @arr;
my @uniqarr;

foreach my $var ( @arr ){
  if ( ! grep( /$var/, @uniqarr ) ){
     push( @uniqarr, $var );
  }
}

Saya pikir ini mungkin cara yang paling mudah dibaca untuk melakukannya.


4

Metode 1: Gunakan hash

Logika: Sebuah hash hanya dapat memiliki kunci unik, jadi iterate over array, tetapkan nilai apa pun untuk setiap elemen array, jadikan elemen sebagai kunci hash itu. Kembalikan kunci hash, ini array unik Anda.

my @unique = keys {map {$_ => 1} @array};

Metode 2: Perpanjangan metode 1 untuk dapat digunakan kembali

Lebih baik membuat subrutin jika kita seharusnya menggunakan fungsi ini beberapa kali dalam kode kita.

sub get_unique {
    my %seen;
    grep !$seen{$_}++, @_;
}
my @unique = get_unique(@array);

Metode 3: Gunakan modul List::MoreUtils

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);

1

Jawaban sebelumnya cukup meringkas cara yang mungkin untuk menyelesaikan tugas ini.

Namun, saya menyarankan modifikasi bagi mereka yang tidak peduli tentang menghitung duplikat, tetapi melakukan perawatan tentang pesanan.

my @record = qw( yeah I mean uh right right uh yeah so well right I maybe );
my %record;
print grep !$record{$_} && ++$record{$_}, @record;

Perhatikan bahwa grep !$seen{$_}++ ...kenaikan yang disarankan sebelumnya $seen{$_}sebelum dinegasikan, sehingga peningkatan terjadi terlepas dari apakah sudah %seenatau belum. Di atas, bagaimanapun, korsleting ketika $record{$_}benar, meninggalkan apa yang terdengar sekali dari%record '.

Anda juga bisa menggunakan kekonyolan ini, yang memanfaatkan autovivifikasi dan keberadaan kunci hash:

...
grep !(exists $record{$_} || undef $record{$_}), @record;

Namun, itu bisa menimbulkan kebingungan.

Dan jika Anda tidak peduli dengan urutan atau jumlah duplikat, Anda dapat melakukan retasan menggunakan irisan hash dan trik yang baru saja saya sebutkan:

...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped

Bagi mereka yang membandingkan: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } Rapi.
stevesliva

0

Coba ini, sepertinya fungsi uniq membutuhkan daftar yang diurutkan untuk berfungsi dengan baik.

use strict;

# Helper function to remove duplicates in a list.
sub uniq {
  my %seen;
  grep !$seen{$_}++, @_;
}

my @teststrings = ("one", "two", "three", "one");

my @filtered = uniq @teststrings;
print "uniq: @filtered\n";
my @sorted = sort @teststrings;
print "sort: @sorted\n";
my @sortedfiltered = uniq sort @teststrings;
print "uniq sort : @sortedfiltered\n";

0

Menggunakan konsep kunci hash yang unik:

my @array  = ("a","b","c","b","a","d","c","a","d");
my %hash   = map { $_ => 1 } @array;
my @unique = keys %hash;
print "@unique","\n";

Output: acbd

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.