2

I would like to iterate over pairs of elements in bash. In Python, this is straigthforward:

x_list = [1, 2, 3, 4]
y_list = ["a", "b", "c", "d"]

for x, y in zip(x_list , y_list):
    # here we have access to the values (i.e. the pairs are assigned to variables):
    result = do_something(x, y)
    print(result)

How can I replicate such a behavior in bash? I would need to store the values of the pairs into variables inside of the for loop, so that I can perform some operations. It should be something like:

for x, y in 1a 2b 3c 4d
  do 
     python script.py --x_value=${x} --y_value=${y}
  done

Note that the elements of the list could be also complicated stuff, such as: x_list = [1e-5, 1e-4, 1e-3] and y_list = [10, 20, 30]

gab
  • 792
  • 1
  • 10
  • 36
  • 2
    How are your elements stored in the first place? The best you can probably do is to iterate over the indices of two arrays that have identical indices. – chepner Mar 18 '22 at 15:59
  • `paste <(echo 1 2 3 4 |tr ' ' \\n) <(echo a b c d | tr ' ' \\n)` – William Pursell Mar 18 '22 at 16:06
  • This is nice, but I would need to store intermediate values into variables. I realize that my question was not clear enough, so I'm updating to script to make it clearer – gab Mar 18 '22 at 16:25

3 Answers3

6

A possible translation of your code would be:

#!/bin/bash

x_list=(1 2 3 4)
y_list=(a b c d)

for i in "${!x_list[@]}"
do
    x=${x_list[i]}
    y=${y_list[i]}
    printf '%q %q\n' "$x" "$y"
done
1 a
2 b
3 c
4 d

But it relies on the fact that the two newly created arrays will have identical indexes. Here's an illustration of the problem:

#!/bin/bash

x_list=(1 2 3 4)
y_list=(a b c d)

unset x_list[0] y_list[1]

for i in "${!list0[@]}"
do
    x=${x_list[i]}
    y=${y_list[i]}
    printf '%q %q\n' "$x" "$y"
done
2 ''
3 c
4 d
Fravadona
  • 13,917
  • 1
  • 23
  • 35
  • Clean and easy to understand, thank you! :) I highlight (as you correctly pointed out) that you need to run this with `bash`, not `sh`, or it won't work – gab Mar 24 '22 at 11:15
1

Here's an option using associative arrays

x_list=(1 2 3 "1e-5" 999)
y_list=("a" "b" "c" "d")

declare -A arr
max=$(( ${#x_list[@]} - 1 ))
# use the smallest size
echo "Sizes: x_list: ${#x_list[@]} y_list: ${#y_list[@]} max: $max"
if [ "${#x_list[@]}" -gt "${#y_list[@]}" ];then
    max=$(( ${#y_list[@]} - 1 ))
fi

for i in $(seq 0 $max);do
    arr[${x_list[$i]}]=${y_list[$i]}
    echo "${x_list[$i]} -> ${y_list[$i]}; ${arr[${x_list[$i]}]}"
done

Result

Sizes: x_list: 5 y_list: 4 max: 4
1 -> a; a
2 -> b; b
3 -> c; c
1e-5 -> d; d

Iterating over keys

for key in "${!arr[@]}"; do
    echo "key=$key, value=${arr[$key]}"
done

Result:

key=1, value=a
key=2, value=b
key=3, value=c
key=1e-5, value=d
LMC
  • 10,453
  • 2
  • 27
  • 52
0

If you've got two non-sparse arrays then the code in Fravadona's answer is a good option. If at least one of the arrays is sparse then this Shellcheck-clean code is one way to do it:

#! /bin/bash -p

x_list=([0]=1 [2]=2 [4]=3 [6]=4)
y_list=([1]=a [3]=b [5]=c [7]=d)

for x in "${x_list[@]}"; do
    IFS= read -r -d '' y
    printf '%q %q\n' "$x" "$y"
done < <(printf '%s\0' "${y_list[@]}")
pjh
  • 6,388
  • 2
  • 16
  • 17