ฉันจะลบรายการที่ซ้ำกันออกจากอาร์เรย์ใน Perl ได้อย่างไร


156

ฉันมีอาร์เรย์ใน Perl:

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

ฉันจะลบรายการที่ซ้ำออกจากอาร์เรย์ได้อย่างไร

คำตอบ:


168

คุณสามารถทำสิ่งนี้ตามที่แสดงในperlfaq4 :

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

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

print "@filtered\n";

ขาออก:

one two three

หากคุณต้องการใช้โมดูลให้ลองใช้uniqฟังก์ชันจากList::MoreUtils


28
โปรดอย่าใช้ $ a หรือ $ b ในตัวอย่างเนื่องจากพวกมันคือกลุ่มดาวมหัศจรรย์แห่งการเรียงลำดับ ()
szabgab

2
มันเป็นmyศัพท์ในขอบเขตนี้ดังนั้นมันก็ดี ที่ถูกกล่าวว่าอาจเป็นชื่อตัวแปรอธิบายเพิ่มเติมสามารถเลือกได้
ephemient

2
@ephemient ใช่ แต่ถ้าคุณมีการเพิ่มการเรียงลำดับในฟังก์ชั่นนี้แล้วมันจะกล้าหาญ$::aและ$::bจะไม่ได้?
vol7ron

5
@BrianVandenberg ยินดีต้อนรับสู่โลกของปี 1987 - เมื่อสิ่งนี้ถูกสร้างขึ้นและคอมไพเลอร์ backword เกือบ 100% สำหรับ perl - ดังนั้นจึงไม่สามารถถูกกำจัดได้
szabgab

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }เป็นการใช้งานที่ดีกว่าเนื่องจากมันสามารถรักษาความสงบเรียบร้อยไว้ได้โดยไม่มีค่าใช้จ่าย หรือดียิ่งขึ้นใช้จากรายการ :: MoreUtils
ikegami

120

เอกสารประกอบ Perl มาพร้อมกับชุดคำถามที่พบบ่อย คำถามของคุณถูกถามบ่อย:

% perldoc -q duplicate

คำตอบคัดลอกและวางจากผลลัพธ์ของคำสั่งด้านบนปรากฏด้านล่าง:

พบได้ใน /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 ฉันจะลบองค์ประกอบที่ซ้ำกันออกจากรายการหรืออาร์เรย์ได้อย่างไร
   (สนับสนุนโดย brian d foy)

   ใช้แฮช เมื่อคุณคิดว่าคำว่า "ไม่ซ้ำกัน" หรือ "ซ้ำ" ให้คิด
   "ปุ่มแฮช"

   หากคุณไม่สนใจคำสั่งขององค์ประกอบคุณก็สามารถทำได้
   สร้างแฮชแล้วแตกกุญแจ มันไม่สำคัญสำหรับคุณ
   สร้างแฮชนั้น: เพียงแค่คุณใช้ "ปุ่ม" เพื่อรับองค์ประกอบที่เป็นเอกลักษณ์

       % hash = map {$ _, 1} @array;
       # หรือแฮชชิ้น: @hash {@array} = ();
       # หรือ foreach: $ hash {$ _} = 1 foreach (@array);

       @unique ของฉัน = keys% hash;

   หากคุณต้องการใช้โมดูลให้ลองใช้ฟังก์ชัน "uniq"
   "รายชื่อ :: MoreUtils" ในบริบทรายการจะส่งคืนองค์ประกอบที่ไม่ซ้ำกัน
   รักษาลำดับของพวกเขาในรายการ ในบริบทสเกลาร์จะส่งคืน
   จำนวนองค์ประกอบที่ไม่ซ้ำกัน

       ใช้รายการ :: MoreUtils qw (uniq);

       ฉัน @unique = uniq (1, 2, 3, 4, 4, 5, 6, 5, 7); # 1,2,3,4,5,6,7
       $ Unique = uniq ของฉัน (1, 2, 3, 4, 4, 5, 6, 5, 7); # 7

   คุณยังสามารถผ่านแต่ละองค์ประกอบและข้ามสิ่งที่คุณเห็น
   ก่อน. ใช้แฮชเพื่อติดตาม ครั้งแรกที่ลูปเห็น
   องค์ประกอบองค์ประกอบนั้นไม่มีคีย์ใน% Seen คำสั่ง "ถัดไป" สร้างขึ้น
   คีย์และใช้ค่าทันทีซึ่งก็คือ "undef" ดังนั้นลูป
   ทำต่อไปที่ "push" และเพิ่มค่าสำหรับคีย์นั้น ต่อไป
   เวลาที่ลูปเห็นองค์ประกอบเดียวกันนั้นมีคีย์อยู่ในแฮชและ
   ค่าสำหรับคีย์นั้นเป็นจริง (เนื่องจากไม่ใช่ 0 หรือ "undef") ดังนั้น
   ข้ามไปที่การทำซ้ำและวนรอบไปที่องค์ประกอบถัดไป

       @unique ของฉัน = ();
       % ของฉันเห็น = ();

       foreach $ elem ของฉัน (@array)
       {
         ถัดไปหากเห็น $ {$ elem} ++;
         กด @unique, $ elem;
       }

   คุณสามารถเขียนสิ่งนี้ได้รวดเร็วยิ่งขึ้นโดยใช้ grep ซึ่งทำเช่นเดียวกัน
   สิ่ง.

       % ของฉันเห็น = ();
       ของฉัน @unique = grep {! $ เห็น {$ _} ++} @array;


17
John iz ใน anzers มาห์ขโมยตัวแทน mah!
brian d foy

5
ฉันคิดว่าคุณควรได้รับคะแนนโบนัสเพื่อดูคำถามจริง ๆ
Brad Gilbert

2
ฉันชอบคำตอบที่ดีที่สุดคือ 95% copy-paste และ 3 ประโยคของ OC เพื่อให้ชัดเจนอย่างสมบูรณ์นี่คือคำตอบที่ดีที่สุด; ฉันเพิ่งพบว่าข้อเท็จจริงที่น่าขบขัน
คู่ปรับ Shot

70

รายการติดตั้ง:: MoreUtilsจาก CPAN

จากนั้นในรหัสของคุณ:

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
ความจริงที่ว่ารายการ :: MoreUtils ไม่ได้รวมอยู่กับความเสียหายของพอร์ตพกพาของโครงการที่ใช้มัน :( (สำหรับฉันจะไม่)
yPhil

3
@ กองหน้า: @dup_listควรอยู่ในuniqสายไม่ใช่@dups
incutonez

@yassinphilip CPAN เป็นหนึ่งในสิ่งที่ทำให้ Perl มีประสิทธิภาพและยอดเยี่ยมเท่าที่จะเป็นไปได้ หากคุณกำลังเขียนโปรเจ็กต์ของคุณโดยยึดตามโมดูลหลักคุณจะเพิ่มขีด จำกัด ขนาดใหญ่บนโค้ดของคุณพร้อมกับโค้ดที่เขียนเป็นเทซึ่งพยายามทำสิ่งที่โมดูลบางตัวทำได้ดีกว่าเพียงเพื่อหลีกเลี่ยงการใช้งาน นอกจากนี้การใช้โมดูลหลักไม่ได้รับประกันอะไรเลยเนื่องจาก Perl รุ่นต่างๆสามารถเพิ่มหรือลบโมดูลหลักออกจากการแจกจ่ายได้ดังนั้นการพกพาจึงยังคงขึ้นอยู่กับว่า
Francisco Zarabozo

24

วิธีการทำตามปกติของฉันคือ:

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

หากคุณใช้แฮชและเพิ่มรายการลงในแฮช นอกจากนี้คุณยังมีโบนัสในการรู้ว่าแต่ละรายการปรากฏในรายการกี่ครั้ง


2
นี่เป็นข้อเสียของการไม่รักษาลำดับเดิมหากคุณต้องการ
Nathan Fellman

มันจะดีกว่าที่จะใช้ชิ้นแทนforeachห่วง:@unique{@myarray}=()
Onlyjob

8

ตัวแปร @array เป็นรายการที่มีองค์ประกอบที่ซ้ำกัน

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

7

สามารถทำได้ง่าย ๆ ด้วย Perl one liner

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.

บล็อก PFM ทำสิ่งนี้:

ข้อมูลใน @in ถูกป้อนเข้าสู่ MAP MAP สร้างแฮชที่ไม่ระบุชื่อ คีย์ถูกแยกออกจากแฮชและป้อนลงใน @out


4

อันสุดท้ายนั้นค่อนข้างดี ฉันแค่บิดมันเล็กน้อย:

my @arr;
my @uniqarr;

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

ฉันคิดว่านี่น่าจะเป็นวิธีที่อ่านง่ายที่สุด


4

วิธีที่ 1: ใช้แฮช

ลอจิก: แฮชสามารถมีได้เฉพาะคีย์ที่ซ้ำกันดังนั้นวนซ้ำแถวลำดับกำหนดค่าใด ๆ ให้กับแต่ละองค์ประกอบของอาร์เรย์ทำให้องค์ประกอบเป็นกุญแจสำคัญของแฮชนั้น ส่งคืนคีย์ของแฮชซึ่งเป็นอาร์เรย์ที่เป็นเอกลักษณ์ของคุณ

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

วิธีที่ 2: การขยายวิธีที่ 1 เพื่อนำมาใช้ใหม่

ดีกว่าที่จะทำให้รูทีนย่อยถ้าเราควรจะใช้ฟังก์ชั่นนี้หลายครั้งในรหัสของเรา

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

วิธีที่ 3: ใช้โมดูล List::MoreUtils

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

1

คำตอบก่อนหน้านี้สรุปวิธีที่เป็นไปได้ในการทำงานนี้ให้สำเร็จ

แต่ผมขอแนะนำให้ปรับเปลี่ยนสำหรับผู้ที่ใครไม่ดูแลเกี่ยวกับการนับซ้ำกัน แต่ทำดูแลเกี่ยวกับคำสั่ง

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

โปรดทราบว่าการgrep !$seen{$_}++ ...เพิ่มขึ้นที่แนะนำก่อนหน้านี้$seen{$_}ก่อนที่จะปฏิเสธดังนั้นการเพิ่มขึ้นจะเกิดขึ้นไม่ว่าจะได้รับ%seenหรือไม่ อย่างไรก็ตามข้างต้นวงจรสั้นเมื่อ$record{$_}เป็นจริงปล่อยให้สิ่งที่เคยได้ยินเมื่อ 'ปิด%record'

คุณสามารถไปเพื่อความไร้สาระนี้ซึ่งใช้ประโยชน์จากการปรับปรุงอัตโนมัติและการมีปุ่มแฮช:

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

อย่างไรก็ตามนั่นอาจนำไปสู่ความสับสน

และหากคุณสนใจที่จะไม่เรียงลำดับหรือนับซ้ำคุณสามารถแฮ็กใหม่โดยใช้แฮชสไลซ์และเคล็ดลับที่ฉันเพิ่งพูดถึง:

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

สำหรับผู้ที่เปรียบเทียบ: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } เรียบร้อย
stevesliva

0

ลองนี้ดูเหมือนว่าฟังก์ชั่น uniq ต้องการรายการที่เรียงลำดับเพื่อให้ทำงานได้อย่างถูกต้อง

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

ใช้แนวคิดของปุ่มแฮชที่ไม่ซ้ำกัน:

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

เอาท์พุท: acbd

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.