From 29af8976144021c4b64f888cfe9d2e02e5d7bb27 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Tue, 7 Jan 2025 16:06:17 -0500 Subject: [PATCH 1/9] WIP Concept: Arrays --- concepts/README.md | 46 ++++- concepts/arrays/.meta/config.json | 10 ++ concepts/arrays/about.md | 1 + concepts/arrays/introduction.md | 267 ++++++++++++++++++++++++++++++ concepts/arrays/links.json | 10 ++ config.json | 5 + 6 files changed, 330 insertions(+), 9 deletions(-) create mode 100644 concepts/arrays/.meta/config.json create mode 100644 concepts/arrays/about.md create mode 100644 concepts/arrays/introduction.md create mode 100644 concepts/arrays/links.json diff --git a/concepts/README.md b/concepts/README.md index 2444df01..8e7a3cc4 100644 --- a/concepts/README.md +++ b/concepts/README.md @@ -54,9 +54,44 @@ The [plan](http://forum.exercism.org/t/bash-syllabus-planning/11952) 8. arrays - numeric and associative - iteration - - namerefs -... +9. arithmetic + +10. more about arrays + + - concatenate to a string with `"${ary[*]}"` + - IFS + - how positional parameters are "array-like" + - passing an array to a function + - by value + ```bash + myfunc() { + local copy=("$@") + ... + } + myfunc "${ary[@]}" + ``` + - indirect variable syntax + ```bash + myfunc() { + local varname=$1 + local tmp="${1}[@]" + local copy=( "${!tmp}" ) + ... + } + myfunc "ary" + ``` + - namerefs + ```bash + myfunc() { + local -n ref=$1 + ... + } + myfunc "ary" + ``` + - sublist syntax `${ary[@]:offeset:length}` + +## More Concepts - brace expansions and how it's different from patterns `/path/to/{foo,bar,baz}.txt` @@ -69,13 +104,6 @@ x. I/O - capturing stdout and stderr - capturing stdout and stderr **into separate variables** -x. arithmetic - -x. variables 2 - - - scope - - attributes: `-i` `-l` `-u` - x. option parsing with getopts x. `set` command and "strict mode" diff --git a/concepts/arrays/.meta/config.json b/concepts/arrays/.meta/config.json new file mode 100644 index 00000000..aec519b8 --- /dev/null +++ b/concepts/arrays/.meta/config.json @@ -0,0 +1,10 @@ +{ + "authors": [ + "glennj" + ], + "contributors": [ + "kotp", + "IsaacG" + ], + "blurb": "Arrays in Bash programs." +} diff --git a/concepts/arrays/about.md b/concepts/arrays/about.md new file mode 100644 index 00000000..46409041 --- /dev/null +++ b/concepts/arrays/about.md @@ -0,0 +1 @@ +# TODO diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md new file mode 100644 index 00000000..f960aa0f --- /dev/null +++ b/concepts/arrays/introduction.md @@ -0,0 +1,267 @@ +# Arrays + +Bash provides three types of parameters: strings, integers and arrays. +Most of the time, string values are sufficient. +But sometimes, you have to store a list of values. +In this case you will use an _array_. + +There are two kinds of arrays: + +* Numerically-indexed arrays map an integer key to a string value. + (Other languages might call this a "list" or an "array".) +* Associative arrays map a string key to a string value. + (Other languages might call this a "map" or a "dictionary".) + +The first few sections of this document will cover numerically-indexed arrays (hereafter simply known as "arrays"). +Details about associative arrays will show up at the end. + +## Declaring and Initializing an Array Variable + +You can initialize an array by assigning a parenthesized list of elements (possibly empty) to an variable. + +```bash +myarray=("one" "two" "three") +``` + +As is standard with every variable assignment, there must be no spaces around the equal sign. + +Note that there are no comma separators between the elements; it is a whitespace-separated list of strings. + +In fact, the amount of whitespace inside the parentheses is completely arbitrary. +Newlines are allowed; use as many as you want to improve readability. + +```bash +myarray=( + "first element" + "second element" + "this is the last element" +) +``` + +Assigning a list of strings to an array variable in this manner will store the first string in index zero, the next string in index one, the next in index two, and so on. + +It is possible to initialize a "sparse array" by specifying the indices you need. + +```bash +raindrops=( [3]="Pling" [5]="Plang [7]="Plong" ) +``` + +~~~~exercism/note +To reiterate: array indices are zero-based. +This snippet outputs "true": + +```bash +myarray=( "one" "two" "three" ) +[[ "${myarray[0]}" == "one" ]] && echo true || echo false +``` +~~~~ + +You can use the `declare` (or `local` inside a function) command to specify that a variable will hold an array. + +```bash +declare -a myarray +``` + +It is more idiomatic to just assign a (perhaps empty) parenthesized list to initialize the variable. + +## Accessing Elements of an Array + +As you saw above, the syntax to access an array element is `${myarray[$index]}`. +The index is given in square brackets. +The curly braces are required. + +~~~~exercism/note +For numerically-indexed arrays, the index is an arithmetic expression. +The Bash manual [states][arithmetic]: + +> Within an arithmetic expression, shell variables may also be referenced by name without using the parameter expansion syntax. + +This means that the `$` is not required for "simple" variable expansion. +You can access elements like this, which is a bit easier to read: + +```bash +i=5 +echo "${myarray[i]}" +# ..............^ +``` + +[arithmetic]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic +~~~~ + +If the given index does not exist in the array, the result of the expansion is an empty string. + +## Assigning to an Array + +To assign to a particular array entry, use this syntax + +```bash +myarray[10]="hello" +``` + +To _append_ to an array, use the `+=` concatenating-assigment operator, and use parentheses: + +```bash +myarray+=( "new element 1" "new element 2" ) +``` + +## Iterating over an Array + +You will typically need to iterate over the _values_ of an array. +Use a for loop (that you saw in the [Looping][looping] concept: + +```bash +myarray=("one" "two" "three") +for value in "${myarray[@]}"; do + echo "$value" +done +``` + +Note the special syntax to expand the array: `"${myarray[@]}"` +This form of parameter expansion substitutes the array values as separate words. +It is somewhat equivalent to: + +```bash +for value in "${myarray[0]}" "${myarray[1]}" ... "${myarray[n]}" +``` + +Except that the `"${myarray[@]}"` form works with sparse arrays as well + +This special expansion, where the index is an ampersand (`@`), is only special within double quotes. + +### Iterating over Array Indices + +Sometimes you need the index as well as the value. +There are a couple of ways to do this. + +1. When you know the indices form an uninterrupted sequence, you can use an _arithmetic_ for loop. + + ```bash + for ((i = 0; i < ${#myarray[@]}; i++)); do + echo "$i -> ${myarray[i]}" + done + ``` + + Note the `${#myarray[@]}` syntax, with the `#` character in front of the array name. + This syntax returns the _length_ of the array. + And since arrays use zero-based indexing, the last index is one less than the array length. + +2. When the array is sparse, you can use this form + + ```bash + for index in "${!raindrops[@]}"; do + echo "$index -> ${raindrops[index]}" + done + ``` + + Note the `${!myarray[@]}` syntax, with the `!` character in front of the array name. + This syntax substitutes the indices of the array as separate words. + For numerically-indexed arrays, the indices are expanded in numerical order. + +## Inspecting an Array + +If you want to look at the contents of an array, use the `declare` command with the `-p` option: + +```bash +a=( "the 'first' element" + second + third + 'the "last" element' ) +declare -p a +``` + +This outputs the contents with canonical shell quoting. + +```bash +declare -a a=([0]="the 'first' element" [1]="second" [2]="third" [3]="the \"last\" element") +``` + +## Associative Arrays + +To reiterate, associative arrays use strings for indices, not numbers. +Everything written above applies for associative arrays, but with small differences. +Generally, we use the term "key" not "index": associative arrays map keys to values. + +### Declaring an Associative Array + +Unlike arrays, associative arrays must be declared. +Use the `-A` option to `declare` (or `local` in a function): + +```bash +declare -A mymap + +myfunc() { + local -A my_local_map + # ... +} +``` + +### Initializing an Associative Array + +The key-value pairs are specified within the parenthesized list: + +```bash +mymap=( [first]=foo [second]=bar [third]=baz ) +declare -p mymap +``` + +This outputs: + +``` +declare -A mymap=([second]="bar" [first]="foo" [third]="baz" ) +``` + +~~~~exercism/note +Be aware that associative arrays are **unordered**. +~~~~ + +The declaration and initialization can happen in one line. + +```bash +declare -A mymap=( [first]=foo [second]=bar [third]=baz ) +``` + +In recent Bash versions, the initialization can use a `(key value key value ...)` list. + +```bash +declare -A mymap=(first foo second bar third baz) +``` + +### Accessing Values + +The same syntax as before: `${mymap[$key]}`. + +Associative array keys are not evaluated in an arithmetic context. +The usual parameter expansion syntax is required. + +### Assigning to an Associative Array + +The same syntax as before: + +```bash +mymap[$key]="$value" +``` + +### Iterating + +You can use a `for value in "${mymap[@]}"` loop. +However, you typically also want the key to go along with the value. + +Use the `"${!mymap[@]}"` syntax we saw above. + +```bash +for key in "${!mymap[@]}"; do + echo "$key -> ${mymap[$key]}" +done +``` + +This outputs + +``` +second -> bar +first -> foo +third -> baz +``` + +To re-emphasize, there is no order to the keys. + +[looping]: https://exercism.org/tracks/bash/concepts/looping diff --git a/concepts/arrays/links.json b/concepts/arrays/links.json new file mode 100644 index 00000000..ed8d8fad --- /dev/null +++ b/concepts/arrays/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://www.gnu.org/software/bash/manual/bash.html#Arrays", + "description": "\"Arrays\" in the bash manual" + }, + { + "url": "https://mywiki.wooledge.org/BashGuide/Arrays", + "description": "\"Arrays\" in the Bash Guide" + } +] diff --git a/config.json b/config.json index 1032f526..82d38cc8 100644 --- a/config.json +++ b/config.json @@ -1253,6 +1253,11 @@ "uuid": "44349c3b-8b64-456a-afa9-aa7fc0e7d618", "slug": "functions", "name": "Functions" + }, + { + "uuid": "ab1409fa-1229-419c-aab4-4f6d634f9f9a", + "slug": "arrays", + "name": "Arrays" } ], "key_features": [ From c08002c32cdc19c93782b8f7868ce6497b6cc4a9 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 13:32:33 -0500 Subject: [PATCH 2/9] review suggestions --- concepts/arrays/introduction.md | 62 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index f960aa0f..66bc882f 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -1,20 +1,27 @@ # Arrays -Bash provides three types of parameters: strings, integers and arrays. -Most of the time, string values are sufficient. -But sometimes, you have to store a list of values. +Bash provides two types of parameters: "scalar" values and arrays. +A scalar value can be a string or a number; it represents a single "thing". + +Most of the time, scalar values are sufficient to contain your data. +But sometimes, you need to store a list of scalar values. In this case you will use an _array_. There are two kinds of arrays: * Numerically-indexed arrays map an integer key to a string value. - (Other languages might call this a "list" or an "array".) + (Other languages might call this a "list" or a "sequence".) * Associative arrays map a string key to a string value. (Other languages might call this a "map" or a "dictionary".) The first few sections of this document will cover numerically-indexed arrays (hereafter simply known as "arrays"). Details about associative arrays will show up at the end. +~~~~exercism/caution +Bash arrays can _only_ store scalar values. +There is no ability to create a two-dimensional array (array of arrays). +~~~~ + ## Declaring and Initializing an Array Variable You can initialize an array by assigning a parenthesized list of elements (possibly empty) to an variable. @@ -38,12 +45,12 @@ myarray=( ) ``` -Assigning a list of strings to an array variable in this manner will store the first string in index zero, the next string in index one, the next in index two, and so on. +Assigning a list of strings to an array variable in this manner will store the first string at index zero, the next string at index one, the next at index two, and so on. It is possible to initialize a "sparse array" by specifying the indices you need. ```bash -raindrops=( [3]="Pling" [5]="Plang [7]="Plong" ) +raindrops=([3]="Pling" [5]="Plang [7]="Plong") ``` ~~~~exercism/note @@ -51,7 +58,7 @@ To reiterate: array indices are zero-based. This snippet outputs "true": ```bash -myarray=( "one" "two" "three" ) +myarray=("one" "two" "three") [[ "${myarray[0]}" == "one" ]] && echo true || echo false ``` ~~~~ @@ -66,7 +73,7 @@ It is more idiomatic to just assign a (perhaps empty) parenthesized list to init ## Accessing Elements of an Array -As you saw above, the syntax to access an array element is `${myarray[$index]}`. +As you saw in the note above, the syntax to access an array element is `${myarray[$index]}`. The index is given in square brackets. The curly braces are required. @@ -101,13 +108,13 @@ myarray[10]="hello" To _append_ to an array, use the `+=` concatenating-assigment operator, and use parentheses: ```bash -myarray+=( "new element 1" "new element 2" ) +myarray+=("new element 1" "new element 2") ``` ## Iterating over an Array You will typically need to iterate over the _values_ of an array. -Use a for loop (that you saw in the [Looping][looping] concept: +Use a for loop (that you saw in the [Looping][looping] concept): ```bash myarray=("one" "two" "three") @@ -124,10 +131,21 @@ It is somewhat equivalent to: for value in "${myarray[0]}" "${myarray[1]}" ... "${myarray[n]}" ``` -Except that the `"${myarray[@]}"` form works with sparse arrays as well +The `"${myarray[@]}"` form works with sparse arrays, expanding to only defined values. This special expansion, where the index is an ampersand (`@`), is only special within double quotes. +#### Other Special Parameter Exapansions + +Here are another couple of bits of special syntax that we need for iterating over an array. +These are described in the manual in [Shell Parameter Expansion][man-expn]. + +To find the _length_ of an array, use `${#myarray[@]}`, with a `#` before the name. +The "length" of an array means the number of elements in it. + +To extract the list of _indices_ of an array, use `"${!myarray[@]}"`, with a `!` before the name and the expansion in double quotes. +For numerically-indexed arrays, the indices are expanded in numerical order. + ### Iterating over Array Indices Sometimes you need the index as well as the value. @@ -142,8 +160,7 @@ There are a couple of ways to do this. ``` Note the `${#myarray[@]}` syntax, with the `#` character in front of the array name. - This syntax returns the _length_ of the array. - And since arrays use zero-based indexing, the last index is one less than the array length. + Since arrays use zero-based indexing, the array's last index is one less than the array length. 2. When the array is sparse, you can use this form @@ -154,18 +171,16 @@ There are a couple of ways to do this. ``` Note the `${!myarray[@]}` syntax, with the `!` character in front of the array name. - This syntax substitutes the indices of the array as separate words. - For numerically-indexed arrays, the indices are expanded in numerical order. ## Inspecting an Array If you want to look at the contents of an array, use the `declare` command with the `-p` option: ```bash -a=( "the 'first' element" - second - third - 'the "last" element' ) +a=("the 'first' element" + second + third + 'the "last" element') declare -p a ``` @@ -183,7 +198,7 @@ Generally, we use the term "key" not "index": associative arrays map keys to val ### Declaring an Associative Array -Unlike arrays, associative arrays must be declared. +Unlike arrays, associative arrays **must** be declared. Use the `-A` option to `declare` (or `local` in a function): ```bash @@ -200,14 +215,14 @@ myfunc() { The key-value pairs are specified within the parenthesized list: ```bash -mymap=( [first]=foo [second]=bar [third]=baz ) +mymap=([first]=foo [second]=bar [third]=baz) declare -p mymap ``` This outputs: ``` -declare -A mymap=([second]="bar" [first]="foo" [third]="baz" ) +declare -A mymap=([second]="bar" [first]="foo" [third]="baz") ``` ~~~~exercism/note @@ -217,7 +232,7 @@ Be aware that associative arrays are **unordered**. The declaration and initialization can happen in one line. ```bash -declare -A mymap=( [first]=foo [second]=bar [third]=baz ) +declare -A mymap=([first]=foo [second]=bar [third]=baz) ``` In recent Bash versions, the initialization can use a `(key value key value ...)` list. @@ -265,3 +280,4 @@ third -> baz To re-emphasize, there is no order to the keys. [looping]: https://exercism.org/tracks/bash/concepts/looping +[man-expn]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion From 4027bf43939670643013668dedc9279bda5b7dc8 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:40:10 -0500 Subject: [PATCH 3/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 66bc882f..1de56d06 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -30,7 +30,7 @@ You can initialize an array by assigning a parenthesized list of elements (possi myarray=("one" "two" "three") ``` -As is standard with every variable assignment, there must be no spaces around the equal sign. +As is the case with every variable assignment, there must be no spaces around the equal sign. Note that there are no comma separators between the elements; it is a whitespace-separated list of strings. From df2c9f0be6b07c4b81ed8aa34915bd7ef2673f46 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:41:14 -0500 Subject: [PATCH 4/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 1de56d06..b244ffd4 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -99,7 +99,7 @@ If the given index does not exist in the array, the result of the expansion is a ## Assigning to an Array -To assign to a particular array entry, use this syntax +To assign to a particular array entry, use this syntax: ```bash myarray[10]="hello" From d1b7ef1c6cd6187b576366c46606bf6eb01190d8 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:41:28 -0500 Subject: [PATCH 5/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index b244ffd4..7102f7d8 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -162,7 +162,7 @@ There are a couple of ways to do this. Note the `${#myarray[@]}` syntax, with the `#` character in front of the array name. Since arrays use zero-based indexing, the array's last index is one less than the array length. -2. When the array is sparse, you can use this form +2. When the array is sparse, you can use this form: ```bash for index in "${!raindrops[@]}"; do From 36ab0aa552ac61b8deb5691a8a920217197a8e50 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:41:56 -0500 Subject: [PATCH 6/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 7102f7d8..f0d11eef 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -243,7 +243,7 @@ declare -A mymap=(first foo second bar third baz) ### Accessing Values -The same syntax as before: `${mymap[$key]}`. +The same syntax as before is used to access elements: `${mymap[$key]}`. Associative array keys are not evaluated in an arithmetic context. The usual parameter expansion syntax is required. From c39f6644b00c3bd79c9d39d170016da49171c3ce Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:42:13 -0500 Subject: [PATCH 7/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index f0d11eef..38e00c62 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -250,7 +250,7 @@ The usual parameter expansion syntax is required. ### Assigning to an Associative Array -The same syntax as before: +Assignment uses the same syntax as before: ```bash mymap[$key]="$value" From d7ba2bc2b4d1c083244d00ed07b7084a2a61b26c Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 14:42:32 -0500 Subject: [PATCH 8/9] Update concepts/arrays/introduction.md Co-authored-by: Isaac Good --- concepts/arrays/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 38e00c62..b1406042 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -258,7 +258,7 @@ mymap[$key]="$value" ### Iterating -You can use a `for value in "${mymap[@]}"` loop. +You can use a `for value in "${mymap[@]}"` loop to iterate. However, you typically also want the key to go along with the value. Use the `"${!mymap[@]}"` syntax we saw above. From 7b8bffd8e847d460788830a100b54914bc199b39 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 19 Jan 2025 15:00:17 -0500 Subject: [PATCH 9/9] add about doc --- concepts/arrays/about.md | 284 +++++++++++++++++++++++++++++++- concepts/arrays/introduction.md | 2 +- 2 files changed, 284 insertions(+), 2 deletions(-) diff --git a/concepts/arrays/about.md b/concepts/arrays/about.md index 46409041..7e8021b0 100644 --- a/concepts/arrays/about.md +++ b/concepts/arrays/about.md @@ -1 +1,283 @@ -# TODO +# About Arrays + +Bash provides two types of parameters: "scalar" values and arrays. +A scalar value can be a string or a number; it represents a single "thing". + +Most of the time, scalar values are sufficient to contain your data. +But sometimes, you need to store a list of scalar values. +In this case you will use an _array_. + +There are two kinds of arrays: + +* Numerically-indexed arrays map an integer key to a string value. + (Other languages might call this a "list" or a "sequence".) +* Associative arrays map a string key to a string value. + (Other languages might call this a "map" or a "dictionary".) + +The first few sections of this document will cover numerically-indexed arrays (hereafter simply known as "arrays"). +Details about associative arrays will show up at the end. + +~~~~exercism/caution +Bash arrays can _only_ store scalar values. +There is no ability to create a two-dimensional array (array of arrays). +~~~~ + +## Declaring and Initializing an Array Variable + +You can initialize an array by assigning a parenthesized list of elements (possibly empty) to an variable. + +```bash +myarray=("one" "two" "three") +``` + +As is the case with every variable assignment, there must be no spaces around the equal sign. + +Note that there are no comma separators between the elements; it is a whitespace-separated list of strings. + +In fact, the amount of whitespace inside the parentheses is completely arbitrary. +Newlines are allowed; use as many as you want to improve readability. + +```bash +myarray=( + "first element" + "second element" + "this is the last element" +) +``` + +Assigning a list of strings to an array variable in this manner will store the first string at index zero, the next string at index one, the next at index two, and so on. + +It is possible to initialize a "sparse array" by specifying the indices you need. + +```bash +raindrops=([3]="Pling" [5]="Plang [7]="Plong") +``` + +~~~~exercism/note +To reiterate: array indices are zero-based. +This snippet outputs "true": + +```bash +myarray=("one" "two" "three") +[[ "${myarray[0]}" == "one" ]] && echo true || echo false +``` +~~~~ + +You can use the `declare` (or `local` inside a function) command to specify that a variable will hold an array. + +```bash +declare -a myarray +``` + +It is more idiomatic to just assign a (perhaps empty) parenthesized list to initialize the variable. + +## Accessing Elements of an Array + +As you saw in the note above, the syntax to access an array element is `${myarray[$index]}`. +The index is given in square brackets. +The curly braces are required. + +~~~~exercism/note +For numerically-indexed arrays, the index is an arithmetic expression. +The Bash manual [states][arithmetic]: + +> Within an arithmetic expression, shell variables may also be referenced by name without using the parameter expansion syntax. + +This means that the `$` is not required for "simple" variable expansion. +You can access elements like this, which is a bit easier to read: + +```bash +i=5 +echo "${myarray[i]}" +# ..............^ +``` + +[arithmetic]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic +~~~~ + +If the given index does not exist in the array, the result of the expansion is an empty string. + +## Assigning to an Array + +To assign to a particular array entry, use this syntax: + +```bash +myarray[10]="hello" +``` + +To _append_ to an array, use the `+=` concatenating-assigment operator, and use parentheses: + +```bash +myarray+=("new element 1" "new element 2") +``` + +## Iterating over an Array + +You will typically need to iterate over the _values_ of an array. +Use a for loop (that you saw in the [Looping][looping] concept): + +```bash +myarray=("one" "two" "three") +for value in "${myarray[@]}"; do + echo "$value" +done +``` + +Note the special syntax to expand the array: `"${myarray[@]}"` +This form of parameter expansion substitutes the array values as separate words. +It is somewhat equivalent to: + +```bash +for value in "${myarray[0]}" "${myarray[1]}" ... "${myarray[n]}" +``` + +The `"${myarray[@]}"` form works with sparse arrays, expanding to only defined values. + +This special expansion, where the index is an ampersand (`@`), is only special within double quotes. + +#### Other Special Parameter Exapansions + +Here are another couple of bits of special syntax that we need for iterating over an array. +These are described in the manual in [Shell Parameter Expansion][man-expn]. + +To find the _length_ of an array, use `${#myarray[@]}`, with a `#` before the name. +The "length" of an array means the number of elements in it. + +To extract the list of _indices_ of an array, use `"${!myarray[@]}"`, with a `!` before the name and the expansion in double quotes. +For numerically-indexed arrays, the indices are expanded in numerical order. + +### Iterating over Array Indices + +Sometimes you need the index as well as the value. +There are a couple of ways to do this. + +1. When you know the indices form an uninterrupted sequence, you can use an _arithmetic_ for loop. + + ```bash + for ((i = 0; i < ${#myarray[@]}; i++)); do + echo "$i -> ${myarray[i]}" + done + ``` + + Note the `${#myarray[@]}` syntax, with the `#` character in front of the array name. + Since arrays use zero-based indexing, the array's last index is one less than the array length. + +2. When the array is sparse, you can use this form: + + ```bash + for index in "${!raindrops[@]}"; do + echo "$index -> ${raindrops[index]}" + done + ``` + + Note the `${!myarray[@]}` syntax, with the `!` character in front of the array name. + +## Inspecting an Array + +If you want to look at the contents of an array, use the `declare` command with the `-p` option: + +```bash +a=("the 'first' element" + second + third + 'the "last" element') +declare -p a +``` + +This outputs the contents with canonical shell quoting. + +```bash +declare -a a=([0]="the 'first' element" [1]="second" [2]="third" [3]="the \"last\" element") +``` + +## Associative Arrays + +To reiterate, associative arrays use strings for indices, not numbers. +Everything written above applies for associative arrays, but with small differences. +Generally, we use the term "key" not "index": associative arrays map keys to values. + +### Declaring an Associative Array + +Unlike arrays, associative arrays **must** be declared. +Use the `-A` option to `declare` (or `local` in a function): + +```bash +declare -A mymap + +myfunc() { + local -A my_local_map + # ... +} +``` + +### Initializing an Associative Array + +The key-value pairs are specified within the parenthesized list: + +```bash +mymap=([first]=foo [second]=bar [third]=baz) +declare -p mymap +``` + +This outputs: + +``` +declare -A mymap=([second]="bar" [first]="foo" [third]="baz") +``` + +~~~~exercism/note +Be aware that associative arrays are **unordered**. +~~~~ + +The declaration and initialization can happen in one line. + +```bash +declare -A mymap=([first]=foo [second]=bar [third]=baz) +``` + +In recent Bash versions, the initialization can use a `(key value key value ...)` list. + +```bash +declare -A mymap=(first foo second bar third baz) +``` + +### Accessing Values + +The same syntax as before is used to access elements: `${mymap[$key]}`. + +Associative array keys are not evaluated in an arithmetic context. +The usual parameter expansion syntax is required. + +### Assigning to an Associative Array + +Assignment uses the same syntax as before: + +```bash +mymap[$key]="$value" +``` + +### Iterating + +You can use a `for value in "${mymap[@]}"` loop to iterate. +However, you typically also want the key to go along with the value. + +Use the `"${!mymap[@]}"` syntax we saw above. + +```bash +for key in "${!mymap[@]}"; do + echo "$key -> ${mymap[$key]}" +done +``` + +This outputs + +``` +second -> bar +first -> foo +third -> baz +``` + +To re-emphasize, there is no order to the keys. + +[looping]: https://exercism.org/tracks/bash/concepts/looping +[man-expn]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index b1406042..3ea30bf7 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -1,4 +1,4 @@ -# Arrays +# Introduction to Arrays Bash provides two types of parameters: "scalar" values and arrays. A scalar value can be a string or a number; it represents a single "thing".