Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine associative arrays in bash?

Does anyone know of an elegant way to combine two associative arrays in bash just like you would a normal array? Here's what I'm talking about:

In bash you can combine two normal arrays as follows:

declare -ar array1=( 5 10 15 )
declare -ar array2=( 20 25 30 )
declare -ar array_both=( ${array1[@]} ${array2[@]} )

for item in ${array_both[@]}; do
    echo "Item: ${item}"
done

I want to do the same thing with two associative arrays, but the following code does not work:

declare -Ar array1=( [5]=true [10]=true [15]=true )
declare -Ar array2=( [20]=true [25]=true [30]=true )
declare -Ar array_both=( ${array1[@]} ${array2[@]} )

for key in ${!array_both[@]}; do
    echo "array_both[${key}]=${array_both[${key}]}"
done

It gives the following error:

./associative_arrays.sh: line 3: array_both: true: must use subscript when assigning associative array

The following is a work-around I came up with:

declare -Ar array1=( [5]=true [10]=true [15]=true )
declare -Ar array2=( [20]=true [25]=true [30]=true )
declare -A array_both=()

for key in ${!array1[@]}; do
    array_both+=( [${key}]=${array1[${key}]} )
done

for key in ${!array2[@]}; do
    array_both+=( [${key}]=${array2[${key}]} )
done

declare -r array_both

for key in ${!array_both[@]}; do
    echo "array_both[${key}]=${array_both[${key}]}"
done

But I was hoping that I'm actually missing some grammar that will allow the one-liner assignment as shown in the non-working example.

Thanks!

like image 987
Benjamin Leinweber Avatar asked Apr 22 '15 17:04

Benjamin Leinweber


2 Answers

I don't have a one-liner either but here is a different 'workaround' that someone might like using string convertion. It's 4 lines, so I'm only 3 semi-colons from the answer you wanted!

declare -Ar array1=( [5]=true [10]=true [15]=true )
declare -Ar array2=( [20]=true [25]=true [30]=true )

# convert associative arrays to string
a1="$(declare -p array1)"
a2="$(declare -p array2)"

#combine the two strings trimming where necessary 
array_both_string="${a1:0:${#a1}-3} ${a2:21}"

# create new associative array from string
eval "declare -A array_both="${array_both_string#*=}

# show array definition
for key in ${!array_both[@]}; do
    echo "array_both[${key}]=${array_both[${key}]}"
done
like image 185
sprague44 Avatar answered Oct 02 '22 15:10

sprague44


this works with bash > 4.3

print_associative_array() {
    # declare a local **reference variable** (hence `-n`) named `array_reference`
    # which is a reference to the value stored in the first parameter
    # passed in
    echo "printing associative array: $1"
    local -n map_ref="$1"

    # print the array by iterating through all of the keys now
    for key in "${!map_ref[@]}"; do
        value="${map_ref["$key"]}"
        echo "  $key: $value"
    done
}
merge_associative_array() {
    # declare a local **reference variable** (hence `-n`) named `array_reference`
    # which is a reference to the value stored in the first parameter
    # passed in
    echo "merging associative arrays: $1 <--- $2"
    local -n map_ref="$1"
    local -n map_ref2="$2"

    # setting the value of keys in the second array, to the value of the same key in the first array
    for key in "${!map_ref2[@]}"; do
        value="${map_ref2["$key"]}"
        echo "  $key: $value"
        map_ref["$key"]="$value"
    done
    print_associative_array "$1"
}

declare -A optionsA
optionsA=( ["--hello"]="HELLO" ["--world"]="WORLD" )
declare -A optionsB
optionsB=( ["--key1"]="keyval" ["--world"]="WORLD2" ["--new-key"]="xyz" )

merge_associative_array "optionsA" "optionsB"

I was inspired by answer of @Gabriel Staples

like image 27
Adán Escobar Avatar answered Oct 02 '22 16:10

Adán Escobar