The thought was array2[c] = c which meant array1 would see "c" and therefore be true
No, array indexing does not work like that. Currently both your arrays are numerically indexed; the index of the item 'c' in array2 is 0
and that's it – there is no additional feature that would automatically make an element have itself as its index.
So the code really did not work as intended in the first place; it just so happened that array2[c]
was interpreted as array2[0]
, because the "c" in numeric context was taken to refer to the variable named 'c' – and since you have no such variable, the result quietly became 0 which coincides with the result you wanted.
But if you tried the same thing with elements ordered a bit differently, you'd notice that ${array2[d]}
also produces the item "c", and ${array2[e]}
also produces "c", and if you do e=1
then ${array2[e]}
produces "d". In all those cases, the array index is interpreted as an arithmetic expression (e.g. it allows ${array2[i+2]}
and similar), not as a string to search for.
With regular, numerically indexed arrays, you would need to loop over the indexes of both arrays to find a matching item:
for (( i = 0; i < ${#array1[@]}; i++ )); do
for (( j = 0; j < ${#array2[@]}; j++ )); do
compare array1[i] to array2[j] here
done
done
This is of course a bit slower since it might need n × m
checks.
To actually make array2[c]
work, you would need to turn array2 into an associative array using declare -A
. Associative arrays (aka 'dicts' or 'maps') use strings as keys/indices, which you need to specify for each element using the [key]=value
syntax.
Just to match the way you were trying to use the arrays, let's make the keys identical to the values:
declare -A array2=( [b]=b [c.sh]=c.sh [d]=d [e]=e )
for (( i = 0; i < ${#array1[@]}; i++ )); do
if [[ ${array1[i]} == "${array2[${array1[i]}]}" ]]; then
echo "item '${array1[i]}' is present in array2"
Now ${array2[e]}
will actually return the value e
and the comparison will work.
But since the goal is to imitate sets, you could also go further and get rid of the (redundant) values entirely, replacing the equality test with a simpler "is value present" test:
declare -A array2=([b]=1 [c.sh]=1 [d]=1 [e]=1)
for (( i = 0; i < ${#array1[@]}; i++ )); do
if [[ ${array2[${array1[i]}]} ]]; then
echo "item '${array1[i]}' is present in array2"
As a completely unrelated simplification, ${!var[@]}
expands to array indices is an easier way to iterate over an array than the C-style loop:
for i in "${!array1[@]}"; do
if [[ ${array2[${array1[i]}]} ]]; then
But in this case you're not using i
for anything except retrieving the value, so you could simplify it further by iterating directly over the values of array1:
for item in "${array1[@]}"; do
if [[ ${array2[$item]} ]]; then
This also works if both arrays are made into dicts, using the same ${!var[@]}
to iterate over the string-based indices:
declare -A array1=([a]=yes [b]=present [c.sh]=sure)
declare -A array2=([b]=1 [c.sh]=1 [d]=1 [e]=1)
for item in "${!array1[@]}"; do
if [[ ${array2[$item]} ]]; then
echo "Item '$item' is present as a key in both arrays"
fi
done