---
tags:
  - shell
  - data-structures
---

# Arrays in Bash

We define an array as follows:

```bash
words=(here are some words)
```

We can also explicitly define an array using `declare` :

```bash
declare -a words=("element1" "element2" "element3")
```

## Index notation

We access specific array elements by their index using the same braces style we
use with variables:

```bash
echo "${words[2]}"

# element3
```

## Iterating through arrays

```bash
for i in "${words[@]}"
do
   echo "$i"
   # or do whatever with individual element of the array
done
# element1 element2 element3
```

Note that `@` here is a special symbol standing for all the members of the
`words` array.

## Sorting arrays

### Sorting an integer array highests to lowest

```bash
sorted_array=($(echo "${array[@]}" | tr " " "\n" | sort -nr))
```

Where `array` is the name of the original array. The sorted array will be stored
in the `sorted_array` array.

The `sort` command sorts the input in reverse numerical order (`-n` for
numerical sort and `-r` for reverse sort). The `tr` command is used to convert
the spaces in the array to newline characters so that each element is sorted on
a separate line.

## Pushing, appending to an array

We use the `+=` shorthand to add elements to a preexisting array:

```bash
preExistingArray=(1 2 3)
preExistingArray+=(4)
echo preExistingArray
# 1 2 3 4

```

> Note that we have to put the item we want to addend within array brackets

## Check if array empty

```sh
an_array=()

if [ -z "${another_array[@]}" ]; then
    echo "Array is empty"
else
    echo "Array is not empty"
fi
```

Here we pass all the elements of the array to a
[test](Test_values_in_Bash.md) condition which
tests for an empty string.

> NB: This will not immediately work in the context of a function. See below.

## Weirdness with functions

When you pass an array as an argument to a
[function](Functions_in_Bash.md) it will not
immediately be understood to be an array.

When we use `$1` to individuate the first function argument this is read as
string. So if you parsed an array argument as `$1`, any logic you have in the
function will work on the assumption that the argument is a string, not an
array.

To get round this we have to effectively _redeclare_ the argument as an array
before running any array logic. We do this through a **nameref** (a reference to
a variable). The nameref resolves to the value of the variable it references.
This allows you to indirectly manipulate the value of the original variable
through the nameref.

A nameref is created with the `-n` flag. The following function uses this method
to check if an array is empty:

```sh
function array_empty() {
    declare -n arr=$1
    if [ ${#arr[@]} -gt 0 ]; then
        echo "array is not empty"
    else
        echo "array is empty"
    fi
}

my_array=()
array_empty "my_array"
```

You'll notice when we invoke the function we pass the array as a string, this
facilitates the nameref operation in the function.

## Associational arrays / maps

With Bash 4 we gained an additional array-like data structure that is key-value
based and similar to maps in other languages.

```bash
declare -A rock=(["win"]="scissors" ["lose"]="paper")
```

We would then individuate a value with `"${rock[win]}"`

## Lists as implicit string arrays

When we use the term **list** in bash, we are not actually referring to a
specific type of data structure. Instead a **list variable** is really just a
normal variable wrapped in quote marks that has strings separated by spaces.
Despite the fact that this is not an actual iterative data structure, we are
still able to loop through variables of this type.

```bash
A_STR_LIST="cat dog hamster"
AN_INT_LIST="1 2 3"
```

To iterate through a list variable, we can use a for loop:

```bash
for ele in $A_STR_LIST; do
    echo $ele
done
```

We are leveraging this aspect of Bash when we
[loop through each character in a string](Strings_in_Bash.md#loop-through-each-character-in-a-string).
