csvから特定キーで重複を取りたい時。
先勝ちのサンプルはたくさんあるが、後勝ちは見かけなかったので書いてみた。
以下のようなCSVデータを例として、
データ先頭から4番目までをキーとし、後勝ちで重複削除する。
all.fbk
OL,FBK-0001,001,HL,CD001,CD002,A1,SF,F,20200323,FBK1
OL,FBK-0001,001,HL,CD001,CD002,A2,SF,F,20200323,FBK2
OL,FBK-0001,001,HL,CD001,CD002,A3,SF,F,20200323,FBK3
NW,FBK-0001,001,HL,CD001,CD002,B1,SF,F,20200323,FBK4
OL,FBK-0001,001,HL,CD001,CD002,B1,SF,F,20200323,FBK5
NW,FBK-0001,001,HL,CD001,CD002,B2,SF,F,20200323,FBK6
NW,FBK-0001,001,HL,CD001,CD002,B3,SF,F,20200323,FBK7
AWK
非常にシンプル。ワンライナーで書けるので良い。
普通に書くと先勝ちになってしまうので、tac
を使って逆順に処理をしている。
どうやらlinux系はtac
、BSD系はtail -r
を使わないといけない模様。
元々の順番で出力したいときは、再度 tac
を通せばいけるかと。
cat all.fbk | tac | awk -F "," '!colname[$1,$2,$3,$4]++{print $0}'
NW,FBK-0001,001,HL,CD001,CD002,B3,SF,F,20200323,FBK7
OL,FBK-0001,001,HL,CD001,CD002,B1,SF,F,20200323,FBK5
- 入力がカンマ区切りのcsvなので
-F ","
を指定(すると、$1から順番に値が割り当てられる) -
colname
はAWKの内部で展開される連想配列 -
colname[$1 $2 $3 $4]
で先頭から4列までを結合し重複キーとする - $0には加工前の文字列が入ってくる
perl
AWKと比べると、こっちの方が何をしたいのか意図を読み取りやすそう。
あとは文字コードの指定が課題となるかも。
perl fbk.pl all.fbk
OL,FBK-0001,001,HL,CD001,CD002,B1,SF,F,20200323,FBK5
NW,FBK-0001,001,HL,CD001,CD002,B3,SF,F,20200323,FBK7
use strict;
use warnings;
use open ":encoding(shiftjis)";
binmode(STDOUT, ":utf8");
# ファイルの読み込み
my @csvDatas = ();
open(DATAFILE, "< " . $ARGV[0]) || die("Error:$!");
while(my $line = <DATAFILE>){
chomp($line);
push @csvDatas, $line;
}
# ユニーク行の抽出
my @uniqLines = ();
my %conflictCheckMap = ();
my $uniqLinesCount = 0;
foreach my $csvData (@csvDatas){
my @lineCols = split(/\,/, $csvData);
my $csvKey = $lineCols[0] . $lineCols[1] . $lineCols[2] . $lineCols[3];
# ハッシュになければ追加、あれば上書き
unless (exists $conflictCheckMap{$csvKey}){
push @uniqLines, $csvData;
$conflictCheckMap{$csvKey} = $uniqLinesCount;
$uniqLinesCount++;
}else{
$uniqLines[$conflictCheckMap{$csvKey}] = $csvData
}
}
print_array(@uniqLines);
# 配列の中身の確認用
sub print_array{
my @csvDatas = @_;
print join("\n", @csvDatas), "\n";
}
- やってることはAWKと同じ
-
my
に続いて変数定義を行う -
@
は配列変数(セットの時は$を使う) -
%
はハッシュ変数 -
$
は通常変数
感想
AWKもperlも初めて書いたけど、こりゃ強力だわ。
ただ、AWKで書ける範囲のものをperlとかで書こうとすると、冗長になりすぎる感がある。
可能な限りAWKを使い、どうしても無理なところだけperlとか他のスクリプト言語使うとよさそう。