3

I know that for associative arrays, there's no inherent order for the array keys:

declare -A map=([a]=b [c]=d [e]=f)
echo "${!map[@]}"   # => e c a (perhaps)

But what about indexed arrays?

Trying this:

declare -a list
for i in {1..1000}; do list[RANDOM]=1; done
echo "${!list[@]}"

It appears that the indices are numerically ordered. But can I rely on that?

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 1
    Can you clarify whether you want to be able to rely on this feature for specific bash versions (i.e. can be proved by checking bash's sources) or on arbitrary future versions (i.e. should be by design / can be proved through documentation)? – Aaron Jul 24 '19 at 15:32
  • When you change the loop to `for i in {1..10}; do echo "${!list[@]}"; list[RANDOM]=1; done` you'd conclude that yes -- some effort seems to be done to sort it numerically. Whether it's reliable...? –  Jul 24 '19 at 17:12
  • @aaron, either way – glenn jackman Jul 24 '19 at 19:42
  • My instinct is that for numerically indexed arrays, the answer is yes, but an authoritative answer should be available from the maintainer, Chet Ramey, at help-bash@gnu.org. – Dennis Williamson Jul 24 '19 at 22:59

2 Answers2

0

Definitive proof (as mentioned) would be to consult the sources (and that could still be subject to variance across versions), because having looked at the documentation it does not make any mention of order of expansion for ${!name[@]} / ${!name[*]} or ${name[@]} / ${name[*]}. However, esp. the bits with basic arithmetic of name=(value1 value2 … ) assignment as well as treatment of negative indices and the fact this works:

$ a=(1 2); a[8]=9; a[5]=6
$ indices=("${!a[@]}"); for i in "${indices[@]}"; do neg_i="$((-1 -${indices[-1]} + i))"; echo -e "a[$i]: ${a[$i]}\t\ta[$neg_i]: ${a[$neg_i]}" ; done
a[0]: 1         a[-9]: 1
a[1]: 2         a[-8]: 2
a[5]: 6         a[-4]: 6
a[8]: 9         a[-1]: 9

shows that the indices of an indexed arrays must by used considering their numerical value for indexing to work from either direction. This could still be implemented without requiring numerical ordering (let alone displaying / expansion).

Stable ordering would also be consistent with other languages where arrays / lists (indexed array in bash terms) are ordered, whereas mapping objects / dictionaries (associative arrays) not necessarily, hence choice to the contrary could be surprising and lead to mistakes. In other word I would assume deviation from such behavior would be a less likely a well jusifiable decision.

Either is indirect reasoning and not a definitive proof though.

Ondrej K.
  • 8,841
  • 11
  • 24
  • 39
0

I spent some time reading the bash source. Indexed arrays are implemented as doubly-linked lists.

Inserting new array elements (ary[i]=value) will walk the list and insert the element such that the indices remain numerically sorted: https://git.savannah.gnu.org/cgit/bash.git/tree/array.c#n548

Extracting the indices (${!ary[@]}) walks the list from the head (the element with the max index) back to the tail (min index) and build a list of indices: https://git.savannah.gnu.org/cgit/bash.git/tree/array.c#n778

So, as currently implemented, I think I can feel confident that the list of indices is indeed ordered.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352