If you don't know a priori which array will be longer, iterate directly over indices from both:
# generate sum for all indices in a *or* b
declare -A finished=( ) # track which indices we've already added
for idx in "${!a[@]}" "${!b[@]}"; do # iterate over indices from *both* arrays
[[ ${finished[$idx]} ]] && continue # skip anything tracked as finished
printf '%s\n' "$idx => $(( ${a[$idx]:-0} + ${b[$idx]:-0} ))"
finished[$idx]=1
done
This works with all kinds of arrays, including sparse arrays or associative arrays, so long as the indices used in arrays a and b match.
Let's take an example:
# 0 1 2 3 4 5 6 7 8 ## indices
a=( 1 2 3 4 5 6 7 8 9 )
b=( 10 20 30 40 50 60 70 80 90 )
# remove "3", at index 2, from array a
unset a[2]
Now, after that unset a[2]
was run, the highest index in a
is still 8
, but ${#a[@]}
is 8
instead of 7
; the rule that the highest index is one less than the number of entries in the array is broken.
However, this answer still works:
0 => 11
1 => 22
3 => 44
4 => 55
5 => 66
6 => 77
7 => 88
8 => 99
2 => 30
We're still aligning the 3
and the 30
by their index values, and treating the absent element 2
from a
as a 0.
When run with:
declare -A a=( [foo]=1 [bar]=1 [baz]=1 ) b=( [bar]=1 [baz]=1 [qux]=1 )
...this emits:
bar => 2
baz => 2
foo => 1
qux => 1
To give a concrete example of a case where this works and many of the other answers don't:
a=( [10234]=20 [25432]=30 )
b=( [10234]=1 [25432]=2 )
...and the result is properly:
10234 => 21
25432 => 32
As another:
declare -A a=( [bob]=20 [jim]=30 )
declare -A b=( [bob]=1 [jim]=2 )
and the proper result:
bob => 21
jim => 32
I'm printing indices in the above to demonstrate more about what the code is doing under-the-hood, but of course you can just remove $idx =>
from the format strings to exclude them.