web-dev-qa-db-ja.com

多次元配列のAwk配列反復

Awkは、配列処理用の連想インデックスを提供します。 1次元配列の要素は繰り返すことができます:

例えば.

for(index in arr1)
  print "arr1[" index "]=" arr1[index]

しかし、この種の2次元配列はどのように行われるのでしょうか。以下に示すような構文は機能しますか?

for(index1 in arr2)
for(index2 in arr2)
   arr2[index1,index2]     
20
cobp

AWKは、SUBSEP変数(0x1c)に保持されている文字とインデックスを連結することにより、多次元配列を偽造します。次のようにsplitを使用して2次元配列を反復処理できます(info gawkファイルの例に基づく):

awk 'BEGIN { OFS=","; array[1,2]=3; array[2,3]=5; array[3,4]=8; 
  for (comb in array) {split(comb,sep,SUBSEP);
    print sep[1], sep[2], array[sep[1],sep[2]]}}'

出力:

2,3,5
3,4,8
1,2,3

ただし、ネストされたforループを使用して、数値的にインデックス付けされた配列を反復処理できます。

for (i = 1; i <= width; i++)
    for (j = 1; j < = height; j++)
        print array[i, j]

GAWKマニュアル からのもう1つの注目すべき情報:

特定のインデックスシーケンスが多次元配列に存在するかどうかをテストするには、1次元配列に使用されるのと同じ演算子(in)を使用します。左のオペランドとして、インデックスのシーケンス全体を括弧で囲んで、コンマで区切って記述します。

     (subscript1, subscript2, ...) in array

Gawk 4は 配列の配列 を追加します。そのリンクから:

for (i in array) {
    if (isarray(array[i])) {
        for (j in array[i]) {
            print array[i][j]
        }
    }
    else
        print array[i]
}

ギザギザの配列を含む、任意の次元の配列の配列をウォークする次の関数については、 配列の配列のトラバーシング も参照してください。

function walk_array(arr, name,      i)
{
    for (i in arr) {
        if (isarray(arr[i]))
            walk_array(arr[i], (name "[" i "]"))
        else
            printf("%s[%s] = %s\n", name, i, arr[i])
    }
} 
36

いいえ、構文

_for(index1 in arr2) for(index2 in arr2) {
    print arr2[index1][index2];
}
_

動作しません。 Awkは多次元配列を真にサポートしていません。あなたが次のようなことをした場合、それは何をしますか

_x[1,2] = 5;
_

2つのインデックス(1と2)を連結して、SUBSEP変数の値で区切られた文字列を作成します。これが「*」に等しい場合は、と同じ効果があります

_x["1*2"] = 5;
_

SUBSEPのデフォルト値は、Ctrl + \に対応する非印刷文字です。これは、次のスクリプトで確認できます。

_BEGIN {
    x[1,2]=5;
    x[2,4]=7;
    for (ix in x) {
        print ix;
    }
}
_

これを実行すると:

_% awk -f scriptfile | cat -v
1^\2
2^\4
_

したがって、質問(多次元配列を反復する方法)に答えるには、単一のfor(a in b)ループを使用するだけですが、axyの部分に分割するには追加の作業が必要になる場合があります。

5
psmears

gawkの現在のバージョン(gnu awk、Linuxのデフォルトであり、どこにでもインストールできます)には、実際の多次元配列があります。

_for(b in a)
   for(c in a[b])
      print a[b][c], c , b
_

関数isarray()も参照してください

3
JJoao

クエリデータを処理する作業でこれをどのように使用するかの例を示します。製品カテゴリおよび顧客IDごとのトランザクションでいっぱいの抽出ファイルがあるとします。

customer_id  category  sales
1111         parts     100.01
1212         parts       5.20
2211         screws      1.33
...etc...

使いやすいawkを使用して、購入した個別の顧客の総数をカウントします。

awk 'NR>1 {a[$1]++} END {for (i in a) total++; print "customers: " total}' \ 
datafile.txt

ただし、各カテゴリで購入した個別の顧客の数を計算すると、2次元の配列が示唆されます。

awk 'NR>1 {a[$2,$1]++} 
      END {for (i in a) {split(i,arr,SUBSEP); custs[arr[1]]++}
           for (k in custs) printf "category: %s customers:%d\n", k, custs[k]}' \
datafile.txt

custs[arr[1]]++の増分は、各category/customer_idペアがawkによって使用される連想配列へのインデックスとして一意であるために機能します。

実際、私はD.Williamsonが述べたように高速でarray[i][j]を実行できるgnuawkを使用しています。しかし、標準のawkでこれを実行できることを確認したかったのです。

2
Merlin

awk(1)はもともと(一部は)C言語の教育ツールになるように設計されており、多次元配列はCとawk(1)の両方でずっとずっと使用されてきました。そのため、POSIX IEEE1003.2がそれらを標準化しました。

構文とセマンティクスを調べるために、「test.awk」という次のファイルを作成する場合:

BEGIN {
  KEY["a"]="a";
  KEY["b"]="b";
  KEY["c"]="c";
  MULTI["a"]["test_a"]="date a";
  MULTI["b"]["test_b"]="dbte b";
  MULTI["c"]["test_c"]="dcte c";
}
END {
  for(k in KEY) {
    kk="test_" k ;
    print MULTI[k][kk]
  }
  for(q in MULTI) {
    print q
  }
  for(p in MULTI) {
    for( pp in MULTI[p] ) {
      print MULTI[p][pp]
    }
  }
}

次のコマンドで実行します。

awk -f test.awk /dev/null

次の出力が表示されます。

date a
dbte b
dcte c
a
b
c
date a
dbte b
dcte c

少なくともLinux Mint 18 Cinnamon 64-bit 4.4.0-21-generic#37-Ubuntu SMP上

1
Bob Makowski