Associative arrays (Bash)

Uit De Vliegende Brigade
Naar navigatie springen Naar zoeken springen
  • Associative arrays are arrays where you can give elements their own name
  • You can use associative arrays to mimic multi-dimensional arrays, with emphasize on mimic
  • Associative arrays are new in Bash 4. To verify which version of Bash you have: bash --version

An associative array means, that the index doesn't have to be a number, but can be symbolic, or a key. E.g.:

declare -A j

j[fruit]=apple
j[color]=blue

Emulating multidimensional arrays

Associative arrays aren't really multidimensional arrays, they only emulate them. As an example:

unset j
declare -A j

j[0,0]="00"
j[0,1]="01"
j[0, 1]="0 1"

for i in "${!j[@]}"
do
   echo "Index: $i - Value: ${j[$i]}"
done

Output:

Index: 0, 1 - Value: 0 1
Index: 0,1 - Value: 01
Index: 0,0 - Value: 00

So, the entry with index [0, 1], is not the same as with index [0,1]. This shows that the whole index is used as one index. Not as something multidimensional.

But does this actually matter? You can include variables for these indices, and I don't really need to make calculations concerning different dimensions.

Number of rows or columns?

A problem with this 'emulated' multi-dimensional arrays: You can't read-out the number of rows or columns, for there are no real rows and columns. Illustration:

# The array below is 3x2
#
unset j
declare -A j

j[1,1]="11"
j[1,2]="12"
j[2,1]="21"
j[2,2]="22"
j[3,1]="31"
j[3,2]="32"

echo "Length: ${#j[@]}"
echo "Complete array: ${j[@]}"

Output:

Length: 6
Complete array: 21 22 31 31 12 11

This can be a problem, when you want to loop over the rows - Can't do that.

Additionally, the syntax with # for reading out the length of an array, illustrates that arrays are basically one-dimensional.

Solutions

  • Always have arrays start with the same number ⇒ I prefer base 1, just as in matrix algebra
  • First dimensions is always rows (x) and the second is columns (y) - Just as in matrix algebra
  • Don't use spaces around the , that separates indices: It messes up language highlighting in Sublime Text
  • Store the dimensions in associated variables (when needed). E.g., in the example above: j_rows=3 and j_columns=2

Examples

As stated above, these are not really multidimensional arrays, just arrays with fancy indices. It doesn't matter if these indices are numerical or symbolic:

declare -A j
j[0,0,0]="000"
j[0,0,1]="001"
j[0,1,0]="010"
j[0,1,1]="011"
j[1,0,0]="100"
j[1,0,1]="101"
j[1,1,0]="110"
j[1,1,1]="111"

echo "${j[0,0,0]} ${j[0,0,1]} ${j[0,1,0]} ${j[0,1,1]}"
echo "${j[1,0,0]} ${j[1,0,1]} ${j[1,1,0]} ${j[1,1,1]}"
unset j

declare -A j

j[fruit,one]=Mango
j[fruit,two]=Apple
j[bird,1]=Cockatail
j[bird,2]=Spottingbird
j[flower,1]=Rose
j[flower,2]=Sunflower
j[animal]=Tiger

for i in "${j[@]}"
do
   echo "Entry: $i"
done

Output:

Entry: Cockatail
Entry: Spottingbird
Entry: Rose
Entry: Sunflower
Entry: Tiger
Entry: Apple
Entry: Mango

Note that the entries seem to appear in arbitrary order

Loop through an array

Again: These are not multidimensional arrays. They only seem that way.....

This works [1]:

declare -A j
j[fruit]=Mango
j[bird]=Cockatail
j[flower]=Rose
j[animal]=Tiger

for i in "${j[@]}"
do
   echo "Entry: $i"
done	

Output:

Entry: Mango
Entry: Rose
Entry: Tiger
Entry: Cockatail

This works:

declare -A j

j[fruit,1]=Mango
j[fruit,2]=Apple
j[bird,1]=Cockatail
j[bird,2]=Spottingbird
j[flower,1]=Rose
j[flower,2]=Sunflower
j[animal,1]=Tiger
j[animal,1]=Mouse

for i in "${j[@]}"
do
   echo "Entry: $i"
done

Output:

Entry: Cockatail
Entry: Spottingbird
Entry: Rose
Entry: Sunflower
Entry: Mouse
Entry: Mango
Entry: Apple

The only problem: The entries seem to be quite random. This is also the case if I insert statement unset assArray1 at the beginning of the script.

Looping through the rows of an associate array: This one isn't as cool as the code before, because the index is given explicitly:

i=1
for ((i; i<=$number_of_sites; i++))
do
   echo "Row ${i}: ${site[$i,1]} &  ${site[$i,2]}"
done

Loop through the index of an array

This works:

declare -A j

j[fruit,1]=Mango
j[fruit,2]=Apple
j[bird,1]=Cockatail
j[bird,2]=Spottingbird
j[flower,1]=Rose
j[flower,2]=Sunflower
j[animal,1]=Tiger
j[animal,1]=Mouse

for i in "${!j[@]}"
do
   echo "Index: $i"
done

With output:

Index: bird,1
Index: bird,2
Index: flower,1
Index: flower,2
Index: animal,1
Index: fruit,1
Index: fruit,2

However, this is totally not exciting: The index was explicitly specified when initialising the array. That's different when you initialise an array through e.g., i= ( 1 2 3 blub 5 ) (or whatever the exact syntax is).

Index + value

Again, not very exciting, but maybe instructive at times:

unset j
declare -A j

j[fruit,one]=Mango
j[fruit,two]=Apple
j[bird,1]=Cockatail
j[bird,2]=Spottingbird
j[flower,1]=Rose
j[flower,2]=Sunflower
j[animal]=Tiger

for i in "${!j[@]}"
do
   echo "Index: $i - Value: ${j[$i]}"
done

Output:

Index: bird,1 - Value: Cockatail
Index: bird,2 - Value: Spottingbird
Index: flower,1 - Value: Rose
Index: flower,2 - Value: Sunflower
Index: animal - Value: Tiger
Index: fruit,two - Value: Apple
Index: fruit,one - Value: Mango

Awk

And for something entirely else: I kinda moved from a spreadsheet to associative arrays. A while ago I saw on YouTube Gary Explains: EVERYONE Needs to Learn a Little Bit of AWK! - Maybe this is what I need?

Sources