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?

David
sumber

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

Greg Hewgill
sumber
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;
John Siracusa
sumber
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);
Ranguard
sumber
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.

Xetius
sumber
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;
Sreedhar
sumber
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

Elang
sumber
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.

jh314
sumber
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);
Kamal Nayan
sumber
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
YenForYang
sumber
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";
saschabeaumont
sumber
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

Sandeep_black
sumber