Roman Dobra
Roman Dobra

Reputation: 3

Combine the array of strings in Bash

I have:

TEXT='1,2
a,b,c
XX,YY'

I need the output like this:

OUT='1,a,XX
1,a,YY
1,b,XX
1,b,YY
1,c,XX
1,c,YY
2,a,XX
2,a,YY
2,b,XX
2,b,YY
2,c,XX
2,c,YY'

Should be some recursion, because the length of lines and lines count is unknown. Explanation would be great, but just the code is also OK.

What I have done so far (trying to use arrays):

TEXT='1,2
a,b,c
XX,YY'

declare -a lines

COUNT=0

while read LINE; do
    lines[$COUNT]=$LINE
    COUNT=$((COUNT+1))
done <<<"$TEXT"

declare -A A

K1_MAX=0
K2_MAX=0

for key in "${!lines[@]}"; do
    IFS=',' read -ra symbols <<<"${lines[$key]}"
    for key2 in "${!symbols[@]}"; do
        if [[ $key -gt $K1_MAX ]]; then K1_MAX=$key; fi
        if [[ $key2 -gt $K2_MAX ]]; then K2_MAX=$key2; fi
        A[$key $key2]=${symbols[$key2]}
    done
done

for key in "${!A[@]}"; do
    echo $key: "${A[$key]}"
true
done | sort

This gives me the output:

0 0: 1
0 1: 2
1 0: a
1 1: b
1 2: c
2 0: XX
2 1: YY

But still can't understand how to combine those values between themselves.

Upvotes: 0

Views: 139

Answers (4)

jhnc
jhnc

Reputation: 16762

#!/bin/bash

TEXT='1,2
a,\n,c
p ,,q

XX,YY'

buildstr(){
    local prefix="$1"

    if [ $# = 1 ]; then
        printf "%s\n" "$prefix"
    else
        IFS=, read -r -a a <<<"$2,"
        shift 2
        for i in "${a[@]}"; do
            buildstr "${prefix:+$prefix,}$i" "$@"
        done
    fi
}

readarray -t a <<<"$TEXT"

OUT=$(buildstr "" "${a[@]}")

declare -p OUT

Upvotes: 2

M. Nejat Aydin
M. Nejat Aydin

Reputation: 10123

A solution using brace expansion and eval (aka evil) might be:

eval printf -v OUT "%s\\\n" {${TEXT//$'\n'/'},{'}}
OUT=${OUT%?} # to strip off the trailing newline character
OUT=${OUT//[\{\}]} # remove '{' and '}' characters, if any
echo "OUT='$OUT'"

or, by the same token, using an array:

eval arr=({${TEXT//$'\n'/'},{'}})
OUT="${arr[*]}"
OUT=${OUT// /$'\n'}
OUT=${OUT//[\{\}]}

Upvotes: 3

user unknown
user unknown

Reputation: 36229

This is simply bash-syntax:

echo -e {1,2}","{a..c}","{XX,YY}"\n"
1,a,XX
 1,a,YY
 1,b,XX
 1,b,YY
 1,c,XX
 1,c,YY
 2,a,XX
 2,a,YY
 2,b,XX
 2,b,YY
 2,c,XX
 2,c,YY

Note that you can't replace values in curly braces with variables, because brace expansion happens before variable substitution.

The .. - syntax is just a from-to and would have worked for 1,2 too, but is one additional character.

you may as well use multi digit numbers, backwards runs, leading zeros and steps different from one:

echo -e {9..11}","{c..a}","{099..101..2}"\n"
9,c,099
 9,c,101
 9,b,099
 9,b,101
 9,a,099
 9,a,101
 10,c,099
 10,c,101
 10,b,099
 10,b,101
 10,a,099
 10,a,101
 11,c,099
 11,c,101
 11,b,099
 11,b,101
 11,a,099
 11,a,101

Very powerful.

Upvotes: 0

markp-fuso
markp-fuso

Reputation: 34504

One awk using a recursive function call:

awk '
BEGIN { FS=OFS="," }

      { for (i=1;i<=NF;i++) {
            val[NR,i]=$i              # val[row#][element#]=elementVal
            max[NR]=NF                # number of elements in a row
        }
      }

function print_tuples(row, out, i) {

    if (row > NR)
        print out
    else
        for (i=1;i<=max[row];i++)
            print_tuples(row+1, out (out ? OFS : "") val[row,i])
}

END   { print_tuples(1) }
' <<< "${TEXT}"

This generates:

1,a,XX
1,a,YY
1,b,XX
1,b,YY
1,c,XX
1,c,YY
2,a,XX
2,a,YY
2,b,XX
2,b,YY
2,c,XX
2,c,YY

Storing in bash variable OUT:

$ OUT=$(awk '... see code above ...')
$ typeset -p OUT
declare -- OUT="1,a,XX
1,a,YY
1,b,XX
1,b,YY
1,c,XX
1,c,YY
2,a,XX
2,a,YY
2,b,XX
2,b,YY
2,c,XX
2,c,YY"

Upvotes: 0

Related Questions