Is it possible to loop over tuples in bash?
As an example, it would be great if the following worked:
for (i,j) in ((c,3), (e,5)); do echo \"$i and $
I believe this solution is a little cleaner than the others that have been submitted, h/t to this bash style guide for illustrating how read can be used to split strings at a delimiter and assign them to individual variables.
for i in c,3 e,5; do
IFS=',' read item1 item2 <<< "${i}"
echo "${item1}" and "${item2}"
done
c=('a' 'c')
n=(3 4 )
for i in $(seq 0 $((${#c[*]}-1)))
do
echo ${c[i]} ${n[i]}
done
Might sometimes be more handy.
To explain the ugly
part, as noted in the comments:
seq 0 2 produces the sequence of numbers 0 1 2. $(cmd) is command substitution, so for this example the output of seq 0 2
, which is the number sequence. But what is the upper bound, the $((${#c[*]}-1))
?
$((somthing)) is arithmetic expansion, so $((3+4)) is 7 etc. Our Expression is ${#c[*]}-1
, so something - 1. Pretty simple, if we know what ${#c[*]}
is.
c is an array, c[*] is just the whole array, ${#c[*]} is the size of the array which is 2 in our case. Now we roll everything back: for i in $(seq 0 $((${#c[*]}-1)))
is for i in $(seq 0 $((2-1)))
is for i in $(seq 0 1)
is for i in 0 1
. Because the last element in the array has an index which is the length of the Array - 1.
A bit more involved, but may be useful:
a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
But what if the tuple is greater than the k/v that an associative array can hold? What if it's 3 or 4 elements? One could expand on this concept:
###---------------------------------------------------
### VARIABLES
###---------------------------------------------------
myVars=(
'ya1,ya2,ya3,ya4'
'ye1,ye2,ye3,ye4'
'yo1,yo2,yo3,yo4'
)
###---------------------------------------------------
### MAIN PROGRAM
###---------------------------------------------------
### Echo all elements in the array
###---
printf '\n\n%s\n' "Print all elements in the array..."
for dataRow in "${myVars[@]}"; do
while IFS=',' read -r var1 var2 var3 var4; do
printf '%s\n' "$var1 - $var2 - $var3 - $var4"
done <<< "$dataRow"
done
Then the output would look something like:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
And the number of elements are now without limit. Not looking for votes; just thinking out loud. REF1, REF2
Using GNU Parallel:
parallel echo {1} and {2} ::: c e :::+ 3 5
Or:
parallel -N2 echo {1} and {2} ::: c 3 e 5
Or:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
Use associative array (also known as dictionary/hashMap):
declare -A pairs=(
[c]=3
[e]=5
)
for key in "${!pairs[@]}"; do
value="${pairs[$key]}"
echo "key is $key and value is $value"
done
Works for bash4.0+.
If you need triples instead of pairs, you can use the more general approach:
animals=(dog cat mouse)
declare -A sound=(
[dog]=barks
[cat]=purrs
[mouse]=cheeps
)
declare -A size=(
[dog]=big
[cat]=medium
[mouse]=small
)
for animal in "${animals[@]}"; do
echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done