Wednesday, March 7, 2012

Another BASH weirdness: associative array really unordered

Associative arrays in bash do not necessarily have their values or keys in the same order even if the keys are identical. This is not a bug (according to the people hanging at #bash), the associative arrays are simply unordered... But, IMO, it is quite contrary to what the programmer expects, because in most programming languages the hash function is static, in bash don't count on it!

Try that for instance:

$ declare -A aa1 aa2
$ for s in test train; do for i in $(seq 0 10); do aa1[$s,$i]=value_of_$s,$i; done; done
$ for key in ${!aa1[@]}; do aa2[$key]=value_of_$key; done
$ echo ${!aa1[@]}
$ echo ${!aa2[@]}

Look carefully... surprise!

You can fix that by sorting the keys and iterating through them, here some code that does that:

# sort words. For instance
# $(sw "truc troc trac")
# returns trac troc truc
sw() {
    echo "$1" | tr " " "\n" | sort | tr "\n" " "
}

# display the values of an associative array according to a static
# order of its keys
sa() {
    eval keys=\${!$1[@]}
    sorted_keys=$(sw "$keys")
    echo $(for k in $sorted_keys; do eval echo \${$1[$k]}; done)
}

Now you can use

$ echo $(sa aa1)
$ echo $(sa aa2)

instead of

$ echo ${aa1[@]}
$ echo ${aa2[@]}

and see that the order is respected.