18

I have to work with an output of a Java tool, which returns a map data structure that looks like HashMap<String, ArrayList<String>. I have to work with BASH and i tried to declare it as an associative array, what is very similar to a map. The declaration of the associative array in bash should be in one line, i try to do this as following.

ARRAY=(["sem1"]=("first name" "second name") ["sem2"]=("third name") ["sem3]=OTHER_LITS)

But this creates the following error:

bash: syntax error near unexpected token `('

I can define this line by line, but i want to have it in one line. How can i define a assoviative array in bash in only one line?

Oni1
  • 1,445
  • 2
  • 19
  • 39

3 Answers3

21

BTW, associative array, dictionary or map - all come into the one abstract data type (let's call it a dictionary).

So, here is the solution for storing array as values in the dictionary of Bash (4+ version).

Note, that array in Bash is a space delimited list of strings (so no any spaces inside the element, i.e. string), so we could write a quoted list:

"firstname middlename secondname"

as a value of the s1 key in our X dictionary:

declare -A X=(
  ['s1']="firstname middlename secondname"
  ['s2']="surname nickname"
  ['s3']="other"
)

Now we can get the value of the s1 key as array:

declare -a names=(${X[s1]})

Variable names now contains array:

> echo $names
firstname

> echo ${names[1]}
middlename

> echo ${#names[@]}
3

Finally, your question part where the strings with spaces were shown:

"first name", "second name"

Let's do a trick - represent a space as a special symbol sequence (it could be just one symbol), for example, double underscores:

"first__name", "second__name"

Declare our dictionary again, but with "escaped" spaces inside array elements:

declare -A X=(
  ['s1']="first__name middle__name second__name"
  ['s2']="surname nickname"
  ['s3']="other"
)

In this case after we get the value of the s1 key as array:

declare -a names=(${X[s1]})

We need to post process our array elements to remove __ the space-replacements to the actual space symbols. To do this we simply use replace commands of Bash strings:

> echo ${names/__/ }
first name

> echo ${names[1]/__/ }
middle name

> echo ${#names[@]}
3
rook
  • 5,880
  • 4
  • 39
  • 51
5

In the absence of multi-dimensional array support in BASH, you can use this word-around associative array. Each key in the associative array is string concatenation of map-index,array-list-index:

# use one line declaration
declare -A array=([sem1,0]="first name" [sem1,1]="second name" [sem2,0]="third name" [sem3,0]="foo bar")

# loop thrpugh the map array
for i in "${!array[@]}"; do echo "$i => ${array[$i]}"; done
sem2,0 => third name
sem1,0 => first name
sem1,1 => second name
sem3,0 => foo bar
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

A more ergonomic solution that doesn't force the manipulation of the keys.

# your data with spaces
array=(1 '2 with space' 3 "4 with space and ' symbol")
declare -p array

# quote it with " and store it, your data can't contain double quote
declare -A associative=([x]=x [array]=$(printf '"%s" ' "${array[@]}"))
declare -p associative

# get your data in another array
eval deserialized_array=(${associative[array]})
declare -p deserialized_array

echo ${deserialized_array[3]}

# or let bash handle everything

# note: array contain data with double quote character
array=(1 '2 with space' 3 "4 with space and ' \" symbol")

declare -A associative=([x]=x [array]=$(declare -p array))
declare -p associative

array=() # make sure data is gone

# get the data in the same array 
eval ${associative[array]}
echo ${array[3]}
  • Nice tricks. But without explanations aimed at actually educating the reader, this isn't that helpful. – huyz Mar 23 '23 at 02:39
  • @huyz, which part of the OT question and the comments explaining what the code is doing do you need to be explain more? – Nadim Khemir Jun 08 '23 at 08:18
  • I wrote that question _before_ you edited your answer and added comments. The answer is fine now – huyz Jun 10 '23 at 06:05