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

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

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

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" }

(等)

答え2

使用幸せ(以前のPerl_6)

~$ raku -e 'my @a; for lines() {.split("|") andthen @a.push: .[0] => ("null".Slip, .[1].split(",").Slip) };  \
            put .invert.invert.join("\n") for [Z=>] @a.map(*.key), @a.map(*.value.rotor(2 => -1) );'   file

上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。つまり、最初のステートメントで配列が宣言されます。 2 番目のステートメントでは、lines()まず bar にキーを取得し、値をコンマで除算して「ハッシュ配列」を読み込みます。 A は各値系列の先頭に追加されます。 Rakuは(ありがたいことに)配列要素を自動的に平面化しないので、を呼び出して平面化を実行します。最後のステートメントでは、「空」で埋められた値が重なって埋められ、各行のキーと値がペアに圧縮されます。キーを関連する各値に拡張するために2回編集してから、またはで印刷します。split|,"null".Sliprotor[Z=>]invertsayput

入力例:

1|a,b,c,d
2|a,b,c,d,e
3|a,b,c

出力例(非逆転列):

1   null a
1   a b
1   b c
1   c d
2   null a
2   a b
2   b c
2   c d
2   d e
3   null a
3   a b
3   b c

.skipヘッダー行を処理する必要がある場合は、簡単な方法はafter呼び出しlines(デフォルトでは行をスキップ)を追加してから、選択したヘッダーを再度追加することですsay()

最後に、OPからの要求は実際には列の順序を変更します。これは最後のステートメントに呼び出しを挿入し、.reverseasの最終準備を作成することによって実行できます。@a.map(*.value.rotor(2 => -1)@a.map(*.value.reverse.rotor(2 => -1)

出力例(逆列):

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
3   c b
3   b a
3   a null

https://raku.org

関連情報