Associative arrays (Bash): verschil tussen versies

Uit De Vliegende Brigade
Naar navigatie springen Naar zoeken springen
Regel 321: Regel 321:
 
== Awk ==
 
== Awk ==
  
And for something entirely else: I kinda moved from a spreadsheet to associative arrays. A while ago I saw on YouTube [https://www.youtube.com/watch?v=jJ02kEETw70 Gary Explains: EVERYONE Needs to Learn a Little Bit of AWK!] - Maybe this is what I need?
+
And for something entirely else: I kinda move from spreadsheets to associative arrays and back. A while ago I saw on YouTube [https://www.youtube.com/watch?v=jJ02kEETw70 Gary Explains: EVERYONE Needs to Learn a Little Bit of AWK!] - Maybe this is what I have been looking?
  
 
== Sources ==
 
== Sources ==

Versie van 29 sep 2022 09:54

  • 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

No real 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.

Iterate over rows?

If there is not really such a thing as rows and columns, than how to iterate over them?

That's probably not so difficult, Just remember that the 'multidimensional index' is just one index, and that you can iterate over it. E.g.:

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

Retrieve a specific value?

How to retrieve a specific value? I suspect that isn't that hard either, and goes similarly as iterating over the array

Solutions

  • When arrays have a numerical index, always start with the same number ⇒ I prefer base 1, just as in matrix algebra
  • For two-dimensional arrays, the first dimensions is always rows (x) and the second is always columns (y) - Just as in matrix algebra
  • Don't use spaces around the , that separates indices: You need a uniform syntax, and using spaces actually 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

Use the symbol ! to refer to an array's index, rather than its content:

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

Length of an array

Use the symbol # to retrieve the length of an array. Since associative arrays are just vectors after all, there is only one dimension to retrieve.

Example:

unset j
declare -A j

j[0,0]="00"
j[0,1]="01"
j[1,0]="10"
j[1,1]="11"
j[2,0]="20"
j[2,1]="21"

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

Output:

Length: 6

Awk

And for something entirely else: I kinda move from spreadsheets to associative arrays and back. A while ago I saw on YouTube Gary Explains: EVERYONE Needs to Learn a Little Bit of AWK! - Maybe this is what I have been looking?

Sources