Bash: Split two strings directly into associative array

Related searches

I have two strings of same number of substrings divided by a delimiter.

I need to create key-value pairs from substrings.

Short example: Input:

firstString='00011010:00011101:00100001'
secondString='H:K:O'
delimiter=':'

Desired result:

${translateMap['00011010']} -> 'H'
${translateMap['00011101']} -> 'K'
${translateMap['00100001']} -> 'O'

So, I wrote:

IFS="$delimiter" read -ra fromArray <<< "$firstString"
IFS="$delimiter" read -ra toArray <<< "$secondString"
declare -A translateMap

curIndex=0
for from in "${fromArray[@]}"; do
    translateMap["$from"]="${toArray[curIndex]}"
    ((curIndex++))
done

Is there any way to create the associative array directly from 2 strings without the unneeded arrays and loop? Something like:

IFS="$delimiter" read -rA translateMap["$(read -ra <<< "$firstString")"] <<< "$secondString"

Is it possible?

A (somewhat convoluted) variation on @accdias's answer of assigning the values via the declare -A command, but will need a bit of explanation for each step ...

First we need to break the 2 variables into separate lines for each item:

$ echo "${firstString}" | tr "${delimiter}" '\n'
00011010
00011101
00100001

$ echo "${secondString}" | tr "${delimiter}" '\n'
H
K
O

What's nice about this is that we can now process these 2 sets of key/value pairs as separate files.

NOTE: For the rest off this discussion I'm going to replace "${delimiter}" with ':' to make this a tad bit (but not much) less convoluted.

Next we make use of the paste command to merge our 2 'files' into a single file; we'll also designate ']' as the delimiter between key/value mappings:

$ paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n')
00011010]H
00011101]K
00100001]O

We'll now run these results through a couple sed patterns to build our array assignments:

$ paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g'
[00011010]=H
[00011101]=K
[00100001]=O

What we'd like to do now is use this output in the typeset -A command but unfortunately we need to build the entire command and then eval it:

$ evalstring="typeset -A kv=( "$(paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g')" )"

$ echo "$evalstring"
typeset -A kv=( [00011010]=H
[00011101]=K
[00100001]=O )

If we want to remove the carriage returns and put on a single line we append another tr at the output from the sed command:

$ evalstring="typeset -A kv=( "$(paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g' | tr '\n' ' ')" )"

$ cat "${evalstring}"
typeset -A kv=( [00011010]=H [00011101]=K [00100001]=O )

At this point we can eval our auto-generated typeset -A command:

$ eval "${evalstring}"

And now loop through our array displaying the key/value pairs:

$ for i in ${!kv[@]}; do echo "kv[${i}] = ${kv[${i}]}"; done
kv[00011010] = H
kv[00100001] = O
kv[00011101] = K

Hey, I did say this would be a bit convoluted! :-)

What's the best way to transform a string into an associative array, In practice, I don't think you would just make an associative array of but neither Bash nor sed play too well with splitting on multiple delimiters;� Associative arrays can be created in the same way: the only thing we need to change is the option used: instead of lowercase -a we must use the -A option of the declare command: $ declare -A my_array This, as already said, it's the only way to create associative arrays in bash. Create indexed arrays on the fly

It is probably not what you expect, but this works:

key_string="A:B:C:D"
val_string="1:2:3:4"
declare -A map
while [ -n "$key_string" ] && [ -n "$val_string" ]; do
    IFS=: read -r key key_string <<<"$key_string"
    IFS=: read -r val val_string <<<"$val_string"
    map[$key]="$val"
done

for key in "${!map[@]}"; do echo "$key => ${map[$key]}"; done

It uses recursion in the read function to reassign the string value. The downside of this method is that it destroys the original strings. The while-loop checks constantly if both strings have a non-zero length.

Next to the above in pure bash, you could any command to generate the associative array. See How do I populate a bash associative array with command output?

This generally looks like:

declare -A map="( $( magic_command ) )"

where the magic_command generates an output like

[key1]=val1
[key2]=val2
[key3]=val3

In this case we use the command:

paste -d "" <(echo "[${key_string//:/]=$'\n'[}]=") \
            <(echo "${val_string//:/$'\n'}")

where we use bash substitution to replace the delimiter with a newline. However, any other magic_command might do. For completion:

key_string="A:B:C:D"
val_string="1:2:3:4"
declare -A map="( $(paste -d "" <(echo "[${key_string//:/]=$'\n'[}]=") \
                                <(echo "${val_string//:/$'\n'}"))      )"
for key in "${!map[@]}"; do echo "$key => ${map[$key]}"; done

Both examples generate the following output

D => 4
C => 3
B => 2
A => 1

Create an associative array from the output of two commands, You could read the whole thing with bash directly, telling read to split on colons: declare -A userarray while IFS=: read -r username password uid gid gecos� In plsql is there a way to split a string into an associative array? Sample string: 'test1:First string, test2: Second string, test3: Third string' INTO. TYPE as_array IS TABLE OF VARCHAR2(50) INDEX BY VARCHAR2(50); a_array as_array; dbms_output.put_line(a_array('test1')); // Output 'First string' dbms_output.put_line(a_array('test2')); // Output 'Second string' dbms_output.put_line(a_array

Not exactly the answer for what you asked but at least it is shorter:

key='00011010:00011101:00100001'
value='H:K:O'
ifs=':'

IFS="$ifs" read -ra keys <<< "$key"
IFS="$ifs" read -ra values <<< "$value"
declare -A kv

for ((i=0; i<${#keys[*]}; i++)); do
    kv[${keys[i]}]=${values[i]}
done

As a side note, you can initialize an associative array in one step with:

declare -A kv=([key1]=value1 [key2]=value1 [keyn]=valuen)

But I don't know how to use that in your case.

Split string into map (Associative Array), A map (maybe Associative Array) where I can iterate through the key/value. Test1.txt Tom is hot Test.sh filename="/directory/Test1.txt" set - A store while IFS= Hi all, I want to split a string into array based on given delimiter, for example:� Bash Associative Array (dictionaries, hash table, or key/value pair) You cannot create an associative array on the fly in Bash. You can only use the declare built-in command with the uppercase "-A" option. The += operator allows you to append one or multiple key/value to an associative Bash array.

If values in your strings won't use spaces i would suggest this approach

firstString='00011010:00011101:00100001'
secondString='H:K:O'
delimiter=':'

declare -A translateMap

firstArray=( ${firstString//$delimiter/' '} )
secondArray=( ${secondString//$delimiter/' '} )

for i in ${!firstArray[@]}; {
    translateMap[firstArray[$i]}]=${secondArray[$i]}
}

Bash, Let's say we have a String parameter and we want to split it by comma my_param ="foo,bar,bash". To split this string by comma we can use; IFS=',' read -r -a� Example-7: Reading multiple string arrays together. Create a bash file named ‘for_list7.sh’ and add the following script. In this example, two string arrays are defined and combined into another array. The outer for loop is used to read the combined array and the inner for loop is used to read each inner array.

Bash associative array examples – Andy Balaam's Blog, declare -A MYMAP # Create an associative array $ MYMAP[foo]=bar # Put a value into an echo ${#MYMAP[@]} # Number of keys in an associative array 2 $ echo bash --version # Must be at least version 4 to have associative arrays GNU where $DB_NAME is the variable pointing to DB name string. Copying associative arrays is not directly possible in bash. The best solution probably is, as already been pointed out, to iterate through the array and copy it step by step. There is another solution which I used to pass variables to functions. You could use the same technique for copying associative arrays:

Bash does not segregate variables by “type”, variables are treated as integer or string depending on the context. Check if Two Strings are Equal # In most cases, when comparing strings you would want to check whether the strings are equal or not.

In addition to removing an element from an array (let's say element #3), we need to concatenate two sub-arrays. The first sub-array will hold the elements before element #3 and the second sub-array will contain the elements after element #3.

Comments
  • I'm afraid it isn't possible to assign everything without a loop (apart from creating a bash command using other tools and then using eval/declare on that). I checked all hits for the keyword associative in the bash manual and couldn't find anything useful.
  • It eval works perfectly! Can we do that it was not ( [00011010]=H, [00100001]=O, [00011101]=K ) But ( [00011010]="H", [00100001]="O", [00011101]="K" ) Even better is ( ["00011010"]="H", ["00100001"]="O", ["00011101"]="K" )
  • sed "s/^/[\"/g; s/]/\"]/g; s/]/]=\"/g;" Mostly completed it. But don't know how to paste last \" properly
  • @kvantour sorry, wasn't copying your answer; you've edited your answer a few times until you came up with the answer that's similar to mine; it took me awhile to compose/post my message ... which appears to have occurred right about the time you (re)(re)edited your answer
  • Be advised that eval is evil and could lead to unexpected results.
  • and echo -e has some issues, too
  • It is a nice idea. But it doesn't work or I'm doing something wrong. Can you explain how it works? How "hash" was declared? Can it be used like this ${hash[$currentKey]} ?
  • I renamed the variable hash into map. It is just the name of your associative array. So you declare it with declare -A map
  • Command version works, but I got error of array assign. At least, you done the most closer thing to what I'm asked. I'll be playing around with declare -A map="( $(paste -d "" <(echo -e "[${string1//:/]=\\n[}]=") \ <(echo -e "${string2//:/\\n}")) )" Thank you!