親と子の関係を達成するためにデータを反転する必要があるシナリオがあります。私のソースデータは次のとおりです。
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番目の要素は文字列null
(split()
最初のインデックス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
式は上記のコードと同じ概要に従いますが、awk
Millerはこれらのヘッダーを読み取って使用する方法を知っているため、ヘッダーを特別なケースとして扱う必要はありません。
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]
}
}
put
Millerを使用すると、サブコマンドの前のオプションを調整してさまざまな形式の結果を生成できます。
きれいに印刷された「禁止された」出力:
$ 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"
.Slip
rotor
[Z=>]
invert
say
put
入力例:
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からの要求は実際には列の順序を変更します。これは最後のステートメントに呼び出しを挿入し、.reverse
asの最終準備を作成することによって実行できます。@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