web-dev-qa-db-ja.com

連想配列BASHの重複キーをマージします

重複するアイテムを含む配列があります。

THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)

上記に基づいて、itemNをキーとして、dataNを値として割り当てる連想配列を作成したいと思います。

私のコードはリストを反復処理し、次のようにkey =>値を割り当てます(リストでいくつかの追加のジョブを実行するため、追加の関数は短縮されます):

function get_items(){
    KEY=$1
    VALUES=()
    shift $2
    for VALUE in "$@"; do
        VALUES[${#VALUES[@]}]="$VALUE"
    done
}

declare -A THE_LIST
for ((LISTID=0; LISTID<${#THE_LIST[@]}; LISTID++)); do
    eval "LISTED_ITEM=(${THE_LIST[$LISTID]})"
    get_items "${LISTED_ITEM[@]}"
    THE_LIST=([$KEY]="${VALUES[@]}")
done

配列を印刷すると、次のようになります。

item1: data1 data2
item1: data2 data3
item2: data4

しかし、代わりに、私は取得したい:

item1: data1 data2 data3
item2: data4

重複するキーをマージする方法と、キーの重複する値を削除する方法が見つかりません。

ここでのアプローチは何でしょうか?

[〜#〜] update [〜#〜]

実際のコードは次のとおりです。

THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)

function get_backup_locations () {
  B_Host="$2"
  B_DIRS=()
  B_DIR=()
  shift 2

  for B_ITEM in "$@"; do
    case "$B_ITEM" in
      -*) B_FLAGS[${#B_FLAGS[@]}]="$B_ITEM" ;;
      *) B_DIRS[${#B_DIRS[@]}]="$B_ITEM" ;;
    esac
  done

  for ((B_IDX=0; B_IDX<${#B_DIRS[@]}; B_IDX++)); do
    B_DIR=${B_DIRS[$B_IDX]}

    ...do stuff here...

  done
}

function get_items () {
  for ((LOCIDY=0; LOCIDY<${#LOCATIONS[@]}; LOCIDY++)); do
    eval "LOCATION=(${LOCATIONS[$LOCIDY]})"
    get_backup_locations "${LOCATION[@]}"
    THE_LIST=([$B_Host]="${B_DIR[@]}")
  done | sort | uniq
}

配列を印刷する場合:

for i in "${!THE_LIST[@]}"; do
    echo "$i : ${THE_LIST[$i]}"
done

私は得る

item1: data1 data2
item1: data2 data3
item2: data4
1
Bart

キーと値が純粋に英数字であることが保証されている場合は、次のようなものが機能する可能性があります。

declare -A output

make_list() {
  local IFS=" "
  declare -A keys                  # variables declared in a function are local by default
  for i in "${THE_LIST[@]}"
  do 
    i=${i//\'/}                    # since everything is alphanumeric, the quotes are useless
    declare -a keyvals=($i)        # split the entry, filename expansion isn't a problem
    key="${keyvals[0]}"            # get the first value as the key
    keys["$key"]=1                 # and save it in keys
    for val in "${keyvals[@]:1}"
    do                             # for each value
      declare -A "$key[$val]=1"    # use it as the index to an array. 
    done                           # Duplicates just get reset.
  done

  for key in "${!keys[@]}"
  do                               # for each key
    declare -n arr="$key"          # get the corresponding array
    output["$key"]="${!arr[*]}"    # and the keys from that array, deduplicated
  done
}

make_list
declare -p output  # print the output to check

入力例を使用すると、次の出力が得られます。

declare -A output=([item1]="data3 data2 data1" [item2]="data4" )

データ項目は故障していますが、重複排除されています。


代わりに、csvモジュールでPythonを使用するのが最善かもしれません。

1
muru

いずれの値にも空白がない場合、このソリューションが機能する可能性があります。 awk連想配列を使用して、declare -Aコマンドを作成します。

#!/bin/bash

THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)

eval "$(\
  for i in "${THE_LIST[@]}"; do
    row=($(eval echo $i))
    echo "${row[@]}"
  done | awk '{ for (i=2; i<=NF; i++) if (seen[$1] !~ $i) { seen[$1]=seen[$1]$i" " } }
    END { for (s in seen) print "declare -A new_list["s"]=\""seen[s] }' | sed 's/[[:space:]]*$/"/'
)"

for i in "${!new_list[@]}"; do
  echo "$i: ${new_list[$i]}"
done

これは印刷します:

item2: data4
item1: data1 data2 data3

値の順序は保持されますが、キーは並べ替えられます。 awkの配列エントリの末尾の空白を削除する方法がわからなかったので、sedを使用して引用符に置き換えましたが、そもそも完全なハックです。

1
m0dular