LoginSignup
0
0

More than 3 years have passed since last update.

csvファイルの重複を後勝ちでなくす

Last updated at Posted at 2020-03-23

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とか他のスクリプト言語使うとよさそう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0