Bashまたはzshで連想配列をソートする正しい方法は何ですか?

Bashまたはzshで連想配列をソートする正しい方法は何ですか?

Bashで連想配列をソートする方法を知りたいです。マニュアルを試してみましたが、並べ替えとは関係がないようです。

現在の解決策は、すべてをエコーし​​、外部プログラムを使用することです。key value | sort -k2

これは私にとって非常に非効率的に見えます。

配列の例は次のとおりです。

A['192.168.2.2']=5
A['192.168.3.2']=1
A['192.168.1.1']=9

最も一般的なIPアドレス2つである192.168.1.1と192.168.2.2を探します。つまり、その値に基づいて配列を並べ替える必要があります。

答え1

Zshにはリストをソートする方法が組み込まれています。しかし、キーとの関連性を維持しながら値をソートする方法はないようです。パラメータ拡張フラグそして下付き文字記号、これは明示的なループが必要であることを意味します。値にnull文字が含まれていないと仮定すると、null文字に関連付けられた値とキーの配列を作成してソートできます。

keys=("${(@k)A}")
values=("${(@v)A}")
combined=()
for ((i=1; i <= $#values; i++)) { combined[i]=($values[i]$'\0'$keys[i]); }
keys_sorted_by_decreasing_value=("${${(@On)combined}#*$'\0'}")
keys_of_the_top_two_values=("${(@)keys_sorted_by_decreasing_value[1,2]}")

@sch編集:最初の4行は次のように単純化できます。

combined=()
for k v ("${(@kv)A}") combined+=($v$'\0'$k)

変数はkeys任意ですが、一貫した順序でvaluesキーと値を含みます。空のキーがなければA書くことができ、値は似ています。キーを事前にソートし、数字でソートするにはマーカーを追加し(前)、昇順にソートするにはマーカーを削除します(この場合は、下付き文字を使用して最初の2つの値を取得できます)。keys=(${(k)A})keys_sorted_by_decreasing_valuen910O[-2,-1]

Ksh93には位置引数のみをソートする方法がありますset -s。これはzshにもありますが、bash 4.2にはありません。値に改行文字や改行文字の前にソートされる制御文字が含まれていないとします。

keys=("${!A[@]}")
combined=()
for ((i=0; i <= ${#keys}; i++)); do combined[i]=(${A[${keys[$i]}]}$'\n'${keys[$i]}); done
set -A sorted -s "${combined[@]}"
top_combined=${sorted[${#sorted[@]}-1]}  # -2 for the next-to-largest, etc.
top_key=${top_combined#*$'\n'}

これはすべて非常に複雑なので、使いやすい外部ソートを使用することをお勧めします。 kshまたはbashのキーまたは値に制御文字が含まれていないとします。

IFS=$'\n'; set -f
keys_sorted_by_decreasing_value=($(
    for k in "${!A[@]}"; do printf '%s\t%s\n' "${A[$k]}" "$k"; done |
    sort | sed $'s/\t.*//'
  ))

答え2

${(kOn)A}zshでは、連想配列()または値()のソートされたキーのリストを取得できますが、ソートされた値の${(On)A}リスト(AFAIK)から直接取得することはできませんが、次のことができます。

typeset -A assoc
assoc=(
  192.168.2.2 5
  192.168.3.2 1
  192.168.1.1 9
  192.168.8.1 9
)
ordered_keys=()

for v ("${(@nO)assoc}") ordered_keys+=("${(@k)assoc[(eR)$v]}")

つまり、値のリスト()を数値()で並べ替え、各値()に一致Oするeyを追加し(正確な一致の場合は、キーではなく値に基づいてリバースリストを取得します)。配列。$assocnforvkeRordered_keys

答え3

KEYでbash連想配列をソートする最良の方法は次のとおりです。いいえ分類してください。

代わりにKEYSリストを取得し、リストを変数にソートしてからリストを繰り返します。例:IPアドレス(キー)とホスト名(値)の配列があるとします。

回避策:KEYで新しいリストを作成し、行に変換し、並べ替え、リストに変換し、それを使用して配列を繰り返します。

declare -A ADDR
ADDR[192.168.1.1]="host1"
ADDR[192.168.1.2]="host2"
etc...

KEYS=`echo ${!ADDR[@]} | tr ' ' '\012' | sort | tr '\012' ' '`
for KEY in $KEYS; do
  VAL=${ADDR[$KEY]}
  echo "KEY=[$KEY] VAL=[$VAL]"
done

答え4

「連想配列」は、一般に、配列のデータが実際の意味を有することを意味する。外部Unixのソートはこの作業に非常に適しており、Unixのソートを超えることができるCプログラマーはほとんどありません。特にビッグデータの場合、UNIXとシェルのすべての機能をカスタマイズ、スライス、分岐、最大限に活用できます。これは、多くのシェルとawkプラットフォームがソートに気にしない理由です。

関連情報