Reputation: 35
can you help me getting rid off the nested for loop? Array may change all the time, as of right now i need to add/remove for loops. Is it possible with pure bash to replace nested for loops with more elegant solution?
#!/bin/bash
i=0
array=(A B C)
places=3
word1=word1
word2=word2
arrC=${#array[*]}
while [ $i -lt $((arrC**places)) ]; do
for a in ${!array[*]}; do
for b in ${!array[*]}; do
for c in ${!array[*]}; do
echo "[$i] ${array[$a]}${word1}${array[$b]}${word2}${array[$c]}"
i=$((i + 1))
done
done
done
done
I've made ABC array for simplicity. Here is more of a real example for an array: array=(I i L l | \ / 1 !)
Upvotes: 0
Views: 130
Reputation: 34324
Looking at an awk
solution that uses a recursive function to allow for a dynamic number of for
loops.
But first a couple mods to the input parameters (to make it easier to pass info into awk
); we'll replace bash
level arrays with simple variables (using a :
as a delimiter for this exercise):
array_s="A:B:C:D"
words_s="${word1}:${word2}"
One awk
idea:
awk -v places="${places}" -v words_s="${words_s}" -v array_s="${array_s}" '
function print_place(place,outstring, j, sfx) { # define but do not pass "j" and "sfx";
# this designates these as local variables
if ( place > places ) # if we haveve exceeded the number of places ...
{ i++ # increment our counter
printf "%s\n", outstring # print our string to stdout
return # and exit this invocation of the function
}
if ( i > total ) return # if we have exceeded total number of outputs
# then exit this invocation of the function
sfx="" # default suffix is "" unless we
if ( place < places ) sfx=words[place] # have not reached the max number of places
for ( j=1 ; j<=n ; j++ ) # loop through arr[] entries
print_place(place+1,outstring arr[j] sfx) # recursive function call for place+1; also pass along
# our growing string
}
BEGIN { n=split(array_s,arr,":") # parse "array_s" into awk array "arr[]"
m=split(words_s,words,":") # parse "words_s" into awak array "words[]"
total=n**places # calculate total number of strings to print
i=0 # init counter
print_place(1,"") # call function
}
'
Some sample runs:
places=3
array_s="A:B:C"
words_s="word1:word2"
awk ...
Aword1Aword2A
Aword1Aword2B
Aword1Aword2C
Aword1Bword2A
Aword1Bword2B
Aword1Bword2C
...snip...
Cword1Cword2A
Cword1Cword2B
Cword1Cword2C # 27 lines of output
##############
places=2
array_s="A:B:C:D"
words_s="word1:word2"
awk ...
Aword1A
Aword1B
Aword1C
Aword1D
Bword1A
Bword1B
Bword1C
Bword1D
Cword1A
Cword1B
Cword1C
Cword1D
Dword1A
Dword1B
Dword1C
Dword1D # 16 lines of output
##############
places=4
array_s="A:B:C:D"
words_s="word1:word2:word3"
awk ...
Aword1Aword2Aword3A
Aword1Aword2Aword3B
Aword1Aword2Aword3C
Aword1Aword2Aword3D
Aword1Aword2Bword3A
Aword1Aword2Bword3B
Aword1Aword2Bword3C
Aword1Aword2Bword3D
Aword1Aword2Cword3A
Aword1Aword2Cword3B
...snip...
Dword1Dword2Cword3D
Dword1Dword2Dword3A
Dword1Dword2Dword3B
Dword1Dword2Dword3C
Dword1Dword2Dword3D # 256 lines of output
Upvotes: 0
Reputation: 241858
Use recursion.
#! /bin/bash
array=(A B C)
words=(word1 word2)
combine () {
local depth=$1
local prefix=${2# }
shift 2
if ((depth == ${#array[@]})) ; then
echo "$prefix"
return
fi
local i
for (( i=1; i<=$#; ++i )) ; do
combine $((depth+1)) "$prefix${!i}${words[depth]}" "${array[@]}"
done
}
combine 0 "" "${array[@]}"
Upvotes: 1