sironekotoroの日記

Perl で楽をしたい

Perl で重複した値をみつける

連休前の出来事

業務で、1 〜 1500 の数字の中から重複しているものを見つける必要があり、その時に 1 分くらいで書いたのが以下の Perl スクリプトです。

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
for my $line (<DATA>) {
    chomp $line;
    $hash{$line}++;
}

for my $num ( sort keys %hash ) {
    if ( $hash{$num} > 1 ) {
        print $num , "\n";    # 3 6
    }
}

__DATA__
1
2
3
3
4
5
6
6
7
8
9
# 以下、1500まで

重複している 3 と 6 だけ表示されます。

こんな感じでちゃんと動いて用はたせたんですが、問題は 1 行書くたびに

「絶対に重複してるものを選び出す関数あるだろ」

「たしか、List::Util ってモジュールに uniq っていう重複のない値を出す関数があったから、その逆もあるに違いない」

「ここでググってみるか、いや、このまま書き続けた方がいいか」

「いや grep とか使ってなんとかできるんじゃないのか」

「というか、元のデータ Google Sheet だからそっちでも重複検出する何かあるだろ何か」

という思いが繰り返し去来することでした。

まぁ、時間あれば調べたんですが、こんときはトラブルシュートでとりあえず速度が必要でした。

重複した値を見つける

これ、ブログのネタになるなーと思って連休に突入したのですが、なんか夜更かししたり映画観に行ったりしているうちに最終日になってしまいました。

ここで書かないと忘れるよなぁ、ってことで書きます。

List::MoreUtils の duplicates

Perl の外部モジュールです。cpanm でサクッとインストールしましょう。

$ cpanm List::MoreUtils

使う関数は duplicates 、そのまま重複って意味です

metacpan.org

#!/usr/bin/env perl
use strict;
use warnings;

use List::MoreUtils qw/duplicates/;

my @duplicates_nums = duplicates(<DATA>);
chomp @duplicates_nums;
print "@duplicates_nums";    # 3 6

__DATA__
1
2
3
3
4
5
6
6
7
8
9

・・・こっちで良かったじゃん。duplicates ってちゃんとやりたい名前もついてて、あとから見た人(数日後の自分)にも優しい。

まぁ次回からはこっち使おう・・・

grep つかう

あんまり短くならなかった。

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
grep {chomp $_;$hash{$_}++} (<DATA>);

for my $num ( sort keys %hash ) {
    if ( $hash{$num} > 1 ) {
        print $num , "\n";
    }
}

__DATA__
1
2
3
3
4
5
6
6
7
8
9

grep の中に全部入れてもいいけど可読性良くない

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
grep {
    chomp $_;
    $hash{$_}++;
    print "$_\n" if $hash{$_} > 1
} (<DATA>);

__DATA__
1
2
3
3
4
5
6
6
7
8
9

Google Sheet の関数

列、または行から直接重複している値のみを抜き出す関数はなさそう。

シート上部のメニューにある「データ」のなかに「重複の削除」というのはあるけど、重複している値のみを表示するってのはなかった。

だもんで、COUNTIF で重複する値を探してソートって感じ?

query 関数とか使えばいいかな?(やってない(SQL でいうところの HAVING 句がないから難しそう

ってことで

重複した値探す時には List::MoreUtils 使うと楽で良いよね、という話でした。