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를 사용하지 마십시오. sort ()의 마법 전역이므로
szabgab

2
그것은의 my이 범위에서 어휘, 그것의 좋은 그렇게. 즉, 더 설명적인 변수 이름을 선택할 수 있습니다.
ephemient

2
예 @ephemient,하지만 당신은이 함수에 정렬을 추가한다면 그것은 것 트럼프 $::a$::b, 그것을하지 않을까요?
vol7ron

5
@BrianVandenberg 1987 년 세계에 오신 것을 환영합니다. 이것이 만들어 졌을 때 – perl의 백 워드 호환성은 거의 100 %이므로 제거 할 수 없습니다.
szabgab

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }무료로 주문을 유지하기 때문에 더 나은 구현입니다. 또는 List :: MoreUtils에서 하나를 사용하십시오.
ikegami

120

Perl 문서에는 멋진 FAQ 모음이 포함되어 있습니다. 자주 묻는 질문 :

% 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 = 키 % hash;

   모듈을 사용하려면 "uniq"기능을 사용해보십시오.
   "List :: MoreUtils". 목록 컨텍스트에서 고유 한 요소를 반환합니다.
   목록에서 순서를 유지합니다. 스칼라 컨텍스트에서
   고유 한 요소 수

       List :: MoreUtils 사용 qw (uniq);

       내 @ 고유 = 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"라는 값을 사용하므로 루프
   "푸시"로 계속 진행하여 해당 키의 값을 증가시킵니다. 다음
   루프가 동일한 요소를 볼 때 그 키는 해시에 존재합니다.
   해당 키의 값은 0이거나 "unef"가 아니기 때문에 true이므로
   next는 해당 반복을 건너 뛰고 루프는 다음 요소로 이동합니다.

       내 @ 고유 = ();
       내 % seen = ();

       내 $ elem을 @each (@array)
       {
         다음에 $ seen {$ elem} ++;
         @unique, $ elem을 밀어;
       }

   grep을 사용하여 더 짧게 작성할 수 있습니다.
   알맞은 것.

       내 % seen = ();
       내 @ 고유 = grep {! $ seen {$ _} ++} @array;


17
mah anzers에있는 John iz는 mah rep를 훔치는!
brian d foy

5
실제로 질문을 찾으려면 보너스 포인트를 받아야한다고 생각합니다.
브래드 길버트

2
가장 좋은 대답은 95 % 복사 붙여 넣기와 OC의 3 문장입니다. 완벽하게 명확하게하기 위해,이 입니다 가장 좋은 대답은, 나는 그 사실이 재미 있다는 것을 알았습니다.
Parthian Shot

70

CPAN의 설치 목록 :: MoreUtils

그런 다음 코드에서 :

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가 :(를 사용하여 / 펄 좀 손해 승 프로젝트의 이동성을 번들로 제공되지 않는다는 사실 (I를위한 하나는하지 않습니다)
yPhil

3
@Ranguard : 통화가 아닌 @dup_list내부에 있어야 함uniq@dups
incutonez

@yassinphilip CPAN은 Perl을 강력하고 강력하게 만드는 것 중 하나입니다. 핵심 모듈만을 기반으로 프로젝트를 작성하는 경우 일부 모듈의 사용을 피하기 위해 훨씬 더 나은 기능을 시도하는 코드를 작성하는 데 코드가 크게 제한됩니다. 또한 코어 모듈을 사용한다고해서 다른 Perl 버전이 배포에서 코어 모듈을 추가하거나 제거 할 수 있으므로 이식성이 여전히 그에 달려 있습니다.
Francisco Zarabozo 2016 년

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

간단한 펄 원 라이너로 할 수 있습니다.

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.