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このスコープでは語彙なので、問題ありません。そうは言っても、おそらくより説明的な変数名を選択することができます。

2
@ephemientはい、でも、この関数に並べ替えを追加する$::a$::b、それは切り捨てられますね。
vol7ron、2012

5
@BrianVandenberg 1987年の世界へようこそ-これが作成されたとき-そして、perlのほぼ100%のバックワード互換性-したがって、それを排除することはできません。
szabgab

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }コストをかけずに順序を維持できるため、より優れた実装です。あるいはもっと良いのは、List :: MoreUtilsからのものを使用することです。
池上

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

   モジュールを使用したい場合は、から「uniq」関数を試してください
   「List :: MoreUtils」。リストコンテキストでは、一意の要素を返します。
   リスト内の順序を保持します。スカラーコンテキストでは、
   一意の要素の数。

       List :: MoreUtils qw(uniq);を使用します。

       my @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にキーがありません。「next」ステートメントは、
   キーとその値をすぐに使用します。これは「undef」なので、ループ
   「プッシュ」を続行し、そのキーの値をインクリメントします。次は
   ループが同じ要素を見ると、そのキーはハッシュに存在し、
   そのキーの値はtrue(0または "undef"ではないため)なので、
   nextはその繰り返しをスキップし、ループは次の要素に進みます。

       私の@unique =();
       私の%seen =();

       私の$ elemをforeach(@array)
       {
         次に$ seen {$ elem} ++の場合;
         @ unique、$ elemをプッシュします。
       }

   同じことを行うgrepを使用して、これをより簡単に書くことができます
   事。

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


17
マーの担当者を盗むマーアンツァーのジョンイズ!
brian d foy 08/10/9

5
実際に質問を調べたらボーナスポイントがもらえると思います。
Brad Gilbert、

2
最良の答えは95%がコピーアンドペーストでOCが3文であることです。完全に明確にするために、これ最良の答えです。私はその事実が面白いと思うだけです。
パルティアンショット

70

CPANからのList :: 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
List :: MoreUtilsがperlと一緒にバンドルされていないという事実は、それを使用するプロジェクトの移植性に損害を与えます:((私は1人もしません)
yPhil

3
@Ranguard:@dup_list内にある必要がありますuniq呼び出し、ない@dups
incutonez

@yassinphilip CPANは、Perlを可能な限り強力かつ優れたものにする1つの要素です。コアモジュールのみに基づいてプロジェクトを作成している場合、コードに大きな制限を課しているだけでなく、一部のモジュールがそれらを使用しないようにするためにはるかに優れた機能を実行しようとするコードを大量に作成しています。また、コアモジュールを使用しても何も保証されません。Perlの異なるバージョンがコアモジュールをディストリビューションに追加またはディストリビューションから削除できるため、移植性は依然としてそれに依存しています。
フランシスコサラボソ2017年

24

これを行う通常の方法は次のとおりです。

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

ハッシュを使用し、アイテムをハッシュに追加する場合。また、各項目がリストに表示される回数を知るというボーナスもあります。


2
これには、必要に応じて元の順序を保持しないという欠点があります。
Nathan Fellman、2014

それは、使用に優れているスライスの代わりに、foreachループ:@unique{@myarray}=()
Onlyjob

8

変数@arrayは重複する要素を持つリストです

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

7

シンプルなPerlワンライナーで実行できます。

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{$_}はtrueの場合は短絡し、一度「オフ」で聞こえた内容を残し%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.