親と子の関係を取得するために、分離された文字列をピボット解除します。

親と子の関係を取得するために、分離された文字列をピボット解除します。

親と子の関係を達成するためにデータを反転する必要があるシナリオがあります。私のソースデータは次のとおりです。

Key_Col |階層

1|a、b、c、d

2|a、b、c、d、e

私の予想結果は次のとおりです。

主な学校 子供
1 D
1 第二
1 第二
1 無効
2 金利 D
2 D
2 第二
2 第二
2 無効

bashスクリプトでこれを達成する方法を教えてください。

私が使用するスクリプトは次のとおりです。

Var="1|a,b,c,d";
for i in $Var
do
 Key=`echo $i |cut -d'|' -f1`
 Hierarchy=`echo $i |cut -d'|' -f2`
 Delim_Count=`echo ${Hierarchy} |awk -F',' '{ print NF-1 }'`
 for (( c=$Delim_Count+1; c>=1; c-- ))
 do
   Parent=`echo ${Hierarchy} |cut -d',' -f$c`
   Prev=`expr $c - 1`
   if [ $Prev -ne 0 ]; then
    Child=`echo ${Hierarchy} |cut -d',' -f${Prev}`
    echo "${Key}|${Parent}|${Child}"
   else
    echo "${Key}|${Parent}|"
   fi
 done
done

ところが問題は100行を超えるとスクリプトを完成するのに長い時間がかかるという点だ。

ベストアンサー1

この種の作業は、通常、テキストまたは構造化データを処理するように設計された言語を使用する方が簡単です。標準テキスト処理ユーティリティを使用したソリューションawkと使用法は次のとおりです。ミラーmlr)は構造化データ(データはCSV形式)操作用に特別に設計されたツールです。


そしてawk

$ cat file
Key_Col|Hierarchy
1|a,b,c,d
2|a,b,c,d,e
$ awk 'BEGIN { OFS=FS="|" } NR == 1 { print $1, "Child", "Parent"; next } { n=split($2,a,","); a[0]="null"; for (i=n;i>0;i--) print $1,a[i],a[i-1] }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null

上記のコードは、各入力行を区切られた一連のawkフィールドに読み込みます。|コンマの2番目のフィールドを配列に分割しますa。配列の0番目の要素は文字列nullsplit()最初のインデックス1を使用した配列の作成したがって、データを上書きせずにインデックス0を使用できることがわかります。次に、配列の終わりから始まりまで繰り返し、最初のフィールドの値と現在の配列要素と配列の前の要素を出力します。最後の反復に達すると、ループ変数の値は1になり、a[1]a[0])が印刷されます。null

タイトルを含む入力の最初の行は異なる方法で処理されます。分割などを行う代わりに、コードは入力の最初のフィールドとChild文字列とParent。条件付きNR==1ブロックはこれを行います。

読みやすくするためにコード形式が変更されましたawk

BEGIN {
    OFS = FS = "|"
}

NR == 1 {
    print $1, "Child", "Parent"
    next
}

{
    n = split($2, a, ",")
    a[0] = "null"
    for (i = n; i > 0; i--)
        print $1, a[i], a[i-1]
}

入力はCSVのように見えるので、CSV認識ツールを使用して処理する方が安全です。 Miller( mlr) は次のツールです。

$ mlr --csv --fs pipe put -q 'm=splitnv($Hierarchy,","); m[0]="null"; for (var i=length(m)-1;i>0;i-=1) { emit {"Key_Col": $Key_Col, "Child": m[i], "Parent": m[i-1] } }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null

Millerput式は上記のコードと同じ概要に従いますが、awkMillerはこれらのヘッダーを読み取って使用する方法を知っているため、ヘッダーを特別なケースとして扱う必要はありません。

m = splitnv($Hierarchy, ",")
m[0] = "null"

for (var i = length(m) - 1; i > 0; i -= 1) {
    emit {
        "Key_Col": $Key_Col,
        "Child": m[i],
        "Parent": m[i-1]
    }
}

putMillerを使用すると、サブコマンドの前のオプションを調整してさまざまな形式の結果を生成できます。

きれいに印刷された「禁止された」出力:

$ mlr --c2p --barred --ifs pipe put ...as above...
+---------+-------+--------+
| Key_Col | Child | Parent |
+---------+-------+--------+
| 1       | d     | c      |
| 1       | c     | b      |
| 1       | b     | a      |
| 1       | a     | null   |
| 2       | e     | d      |
| 2       | d     | c      |
| 2       | c     | b      |
| 2       | b     | a      |
| 2       | a     | null   |
+---------+-------+--------+

JSON:

$ mlr --c2j --ifs pipe put ...as above...
{ "Key_Col": 1, "Child": "d", "Parent": "c" }
{ "Key_Col": 1, "Child": "c", "Parent": "b" }
{ "Key_Col": 1, "Child": "b", "Parent": "a" }
{ "Key_Col": 1, "Child": "a", "Parent": "null" }
{ "Key_Col": 2, "Child": "e", "Parent": "d" }
{ "Key_Col": 2, "Child": "d", "Parent": "c" }
{ "Key_Col": 2, "Child": "c", "Parent": "b" }
{ "Key_Col": 2, "Child": "b", "Parent": "a" }
{ "Key_Col": 2, "Child": "a", "Parent": "null" }

(等)

おすすめ記事