重複値に基づいてリストをマージ

重複値に基づいてリストをマージ

私は次のようにデータを整理しました。

a
a f
b
c
c e
d
f z

デフォルトでは、この行はすべて同じ項目のエイリアスのリストなので、マージする必要があります。これは単純化されました。実際の状況で重要な場合は、移動されたファイルパスをカバーしており、どのファイルパスが本質的に同じであるかを知る必要があります。入力には、初期ファイルの列1つとファイルの名前変更の列2つがあります。次の出力を見つけます。

a f z
b
c e
d

これは一般的なLinuxシステムのbashスクリプトのため、ほとんどの標準ツールが可能です。これまで、このトピックを扱う他の質問でいくつかのawkスクリプトを試しましたが、良い結果が見つかりませんでした。

答え1

Awk解決策:

awk '{ 
         if (NF == 2) {
             if ($1 in r) { 
                 a[r[$1]] = a[r[$1]] OFS $2; next 
             } 
             a[$1] = $2; r[$2] = $1; 
         } 
         else a[$1]; 
     }
     END{ for (i in a) print i, a[i]  }' file
  • NF == 2- 2つのフィールドを持つレコードを表す条件(NF- 合計フィールド数)
  • a- 元のファイル名と名前が変更されたバージョンの合計または関係などの「スタンドアロン」ファイル名(名前が変更されていない)を含む配列(b例:)da -> f
  • r- 逆関係「名前が変更されたファイル名」 - >「初期ファイル名」を含む配列(例f -> a:)

出力:

a f z
b 
c e
d 

一部のファイル名を複数回変更できる場合は、次の拡張ソリューションを使用してください。

awk '{ 
         if (NF == 2) {
             if ($1 in r) { 
                 a[r[$1]] = a[r[$1]] OFS $2; r[$2] = r[$1];
             } 
             else { a[$1] = $2; r[$2] = $1 } 
         } 
         else a[$1]; 
     }
     END{ for (i in a) print i, a[i]  }' file

答え2

gawk '
{
    arr[cnt][0] = $1    
    arr[cnt++][1] = $2  
}
END {
    for(i = 0; i < cnt; i++) {
        if(!arr[i][0]) continue

        next_name = arr[i][0]

        for(j = i; j < cnt; j++) {
            if(arr[j][0] != next_name) continue

            if(arr[j][1]) {
                next_name = arr[j][1]
                delete arr[j]
            }
            printf "%s ", next_name

        }
        print ""
    }
}' cnt=0 input.txt

入力する(テストが複雑)

u
a
a f
b
c
c e
d
c
f g
g a
a i
i j
a
a z
z w

出力

u 
a f g a i j 
b 
c e 
d 
c 
a z w 

関連情報