Ifeoma
Ifeoma

Reputation: 21

how do i loop through an array of numbers and update the array in bash using the array values

I am trying to increase the values of an array of integers using a variable, but it doesn't seem to be accessing the array values properly. It is to start from 0 in an ascending order, while incrementing the current array value at each iteration. I tried this:

array=(2 0 1)
tag=(H H H)
count=0

for i in ${array[@]}
do
    if [[ "$array[$i]"="$count" ]]
    then
        array[$i]=$((${array[$i]}+1)) 
        tag[$i]="C"
    fi
count=$(($count + 1))
done

Instead it kept updating from 2, then 1, before 0. I want it to start from 0, since that is the index that is equal to count. This was the output.

 0       H H C
         2 0 2

 1       C H C
         3 0 2

 2       C C C
         3 1 2

Upvotes: 0

Views: 1575

Answers (4)

Bach Lien
Bach Lien

Reputation: 1060

I guess that you need two nesting loops in this case.

  • The outer loop is to scan the array once using count.
  • The inner loop is to identify the element that have value equal to count (0, then 1, then 2).
  • The tag array is to avoid increasing the same element twice.

OK, putting all that in code:

#!/bin/bash
array=(2 0 1)
tag=(H H H)
n=${#array[*]}                             ## array size
echo "${array[@]@A}; ${tag[@]@A};"         ## for testing
for (( count=0; count<n; ++count )); do
  for (( i=0; i<n; ++i )); do
    if [[ ${array[$i]} -eq $count && ${tag[$i]} = "H" ]]; then
      (( array[i]++ ))
      tag[$i]="C"                          ## C=changed
      break                                ## break inner loop
    fi
  done
  echo "${array[@]@A}; ${tag[@]@A};"       ## for testing
done

Results:

declare -a array=([0]="2" [1]="0" [2]="1"); declare -a tag=([0]="H" [1]="H" [2]="H");
declare -a array=([0]="2" [1]="1" [2]="1"); declare -a tag=([0]="H" [1]="C" [2]="H");
declare -a array=([0]="2" [1]="1" [2]="2"); declare -a tag=([0]="H" [1]="C" [2]="C");
declare -a array=([0]="3" [1]="1" [2]="2"); declare -a tag=([0]="C" [1]="C" [2]="C");

(sorry for my broken English)

Upvotes: 0

Vab
Vab

Reputation: 442

FYI: Bash indexing starts from 0 (so to change the 2-nd element you should consider index No. 1)

Note: Here the 0 in array is somehow equal to 3 - last element! (so, using 3 would be more user-friendly).

Assuming the $array values define the numbering element in $tag to be changed (ex. 2 for 2nd, 3 for 3rd, and so on) accordingly, the following script will do the job.

script.sh

#!/bin/bash
#array=(2 0 1) 

# Advantage of the logic of this script is that you may have more elements
# in $tag array but are able to change only a few of them that is mentioned in $array.
array=(2 3 1) # Change 2nd, 3rd, 1st elements accordingly.
tag=(H H H)

for i in `seq 1 ${#array[@]}` ;do
  (( i-- ))
  ii="${array[i]}"
  (( ii-- ))
  tag[$ii]="C"
  echo -e "$i:\t${tag[@]}"
done

Output of the script.sh:

0:  H C H
1:  H C C
2:  C C C

Update:

Just seen your comment, will try to update my script.

I want it to start from the array value 0, which is is the middle. So it should look like this, H C H > H C C > C C C

Script Updated, please check again!

Upvotes: 0

Andrej Podzimek
Andrej Podzimek

Reputation: 2778

In the loop iteration you are using array values instead of array indices, which is the core of the problem. So the TL;DR is: "${!array[@]}".

array=(2 0 1)
tag=(H H H)
echo "${array[@]@A}; ${tag[@]@A};"

for index in "${!array[@]}"; do
  ((++array[index]))
  tag[index]=C
  echo "${array[@]@A}; ${tag[@]@A};"
done

Output:

declare -a array=([0]="2" [1]="0" [2]="1"); declare -a tag=([0]="H" [1]="H" [2]="H");
declare -a array=([0]="3" [1]="0" [2]="1"); declare -a tag=([0]="C" [1]="H" [2]="H");
declare -a array=([0]="3" [1]="1" [2]="1"); declare -a tag=([0]="C" [1]="C" [2]="H");
declare -a array=([0]="3" [1]="1" [2]="2"); declare -a tag=([0]="C" [1]="C" [2]="C");

Upvotes: 3

chuckj
chuckj

Reputation: 155

One problem is that the values you are retrieving with the for loop are integers that are being used as indexes to update the values of the array. A second problem is that your conditional statement is actually an assignment, so its exit code is always 0 (true), so $count, though incrementing, affects nothing.

First time through, $i==2, the third element of array is incremented (array[2]==2), the third element of the tag array is set to changed

The second time through, $i==0, the first element of array is incremented (array[0]==3), the first element of tag array is set to changed.

The third time through, $i==1 (see comment below), the second element of array is incremented (array[1]==1), and the second element of the tag array is set to changed.

Promised comment: In the third iteration, other languages would have $i==2 because array[2] had been incremented in the first loop. Bash is apparently iterating over the original values of the array, despite subsequent changes.

I think what you want to do is:

declare -a array=(2 0 1)
declare -a tag=("here" "here" "here")
declare -i count=0
declare -i i

echo "$count:  ${array[*]}"
echo "   ${tag[*]}"

for (( i=0; i<${#array[*]}; ++i )); do
    (( array[i]++ ))   # no need for '$' within (( ))
    tag[$i]="changed"
    (( count++ ))
    echo "$count:  ${array[*]}"
    echo "   ${tag[*]}"
done

I didn't include your conditional because I can't figure out what you're trying to do with it.

I added the echo statements to create output similar to the output you claimed in your example.

Upvotes: 0

Related Questions