キーと値のペアを持つループの場合、キーはソート順を維持しません。

キーと値のペアを持つループの場合、キーはソート順を維持しません。

私は次のスクリプトを持っており、この場合、forループが私が指定した順序で実行されることがわかりました。 2018年、2019年、2020年になると予想されたが、結果は2019年、2018年、2020年になった。

シェルスクリプトにこれに特別な理由があり、順序を保存する方法はありますか?

#!/bin/sh

declare -A arr
arr=( ["2018"]=5%12 ["2019"]=1%12 ["2020"]=1%2 )
INPUT_MONTH=$2
INPUT_YEAR=$1

#For loop to iterate the year(key) value of array
for year in ${!arr[@]}; do
  echo  ${year} ${arr[${year}]}
  MONTH_RANGE=${arr[${year}]}
  if [ ${year} -ge ${INPUT_YEAR} ]; then
    START_MONTH=$(echo "${MONTH_RANGE}" | cut -d'%' -f 1)
    END_MONTH=$(echo "${MONTH_RANGE}" | cut -d'%' -f 2)
    # input year is equal and input month is different from default start one.
    if [ "${year}" == "${INPUT_YEAR}" ]; then
      START_MONTH=$INPUT_MONTH
    fi
    for mon in $(seq $START_MONTH $END_MONTH); do
      echo "Process year:month <=> ${year}:${mon}"
    done;
  else
    continue;
  fi
done;

出力:

2019 1%12
Process year:month <=> 2019:1
Process year:month <=> 2019:2
Process year:month <=> 2019:3
Process year:month <=> 2019:4
Process year:month <=> 2019:5
Process year:month <=> 2019:6
Process year:month <=> 2019:7
Process year:month <=> 2019:8
Process year:month <=> 2019:9
Process year:month <=> 2019:10
Process year:month <=> 2019:11
Process year:month <=> 2019:12
2018 5%12
Process year:month <=> 2018:4
Process year:month <=> 2018:5
Process year:month <=> 2018:6
Process year:month <=> 2018:7
Process year:month <=> 2018:8
Process year:month <=> 2018:9
Process year:month <=> 2018:10
Process year:month <=> 2018:11
Process year:month <=> 2018:12
2020 1%2
Process year:month <=> 2020:1
Process year:month <=> 2020:2

答え1

Bashでdeclare -A arr以下を宣言してください。関連大量に。連想配列のキーはハッシュされ、巡回順序は${!arr[@]}保証されませ1。

$ declare -A arr
$ arr=( ["2018"]=5%12 ["2019"]=1%12 ["2020"]=1%2 )
$ for year in "${!arr[@]}"; do printf '%s: %s\n' "${year}" "${arr[${year}]}"; done
2019: 1%12
2018: 5%12
2020: 1%2

代わりdeclare -a arr索引配列は期待どおりに整列する必要があります。

$ declare -a arr
$ arr=( [2018]=5%12 [2019]=1%12 [2020]=1%2 )
$ for year in "${!arr[@]}"; do printf '%s: %s\n' "${year}" "${arr[${year}]}"; done
2018: 5%12
2019: 1%12
2020: 1%2

キー(年)は数字なので、この場合インデックス配列を使用しない理由がないようです。


引用:

  1. 連想配列の順序を維持する方法は?

答え2

この場合、キーは数字なので、Steeldriverのソリューションを使用できます。

一般的なアプローチは、キーシーケンスのための追加の配列を提供することです。

declare -a ordered_keys
ordered_keys=(2018 2019 2020)
# or dynamically
ordered_keys=($(for key in "${!arr[@]}"; do printf '%s\n' "$key"; done | sort))

その後、交換

for year in ${!arr[@]}; do

あなたはそうです

for key in "${ordered_keys[@]}"; do

関連情報