2

I have a text file that looks like:

1 aaaa
2 bbbb
3 cccc
4 dddd
2 eeee
2 ffff
4 gggg

I would like to map these into some sort of associative array so that I can access, for example, all the values associated with the key 2 and all the values associated with the key 4, etc.:

1->aaaa
2->bbbb,eeee,ffff
3->cccc
4->dddd,gggg

I haven't been able to figure out how to do this with 'declare -A MYMAP'. Is there some easy way to do this?

--------update--------

my key/value pairs look like this actually:

bb126.B1 bb126.1.ms.01
bb126.B2 bb126.1.ms.02
bb126.B3 bb126.1.ms.03
bb126.B4 bb126.1.ms.04
6
  • What is the exact error? Commented Jan 8, 2015 at 3:33
  • @l19 Well, I only know how to map one value to one key with declare MYMAP. How do I map multiple values to one key? Commented Jan 8, 2015 at 3:36
  • What do you need to do with the values later? Do you need to be able to get at them individually or just as one large string? Commented Jan 8, 2015 at 3:37
  • I have another file with a subset of the keys. I'm going to iterate over those keys in the other file and replace them with their sets of associated values (comma delimited). Commented Jan 8, 2015 at 3:41
  • Do the values ever contain whitespace? Commented Jan 8, 2015 at 3:50

2 Answers 2

6

Here's a solution with Shell Parameter Expansion and Associative Arrays:

# store
declare -A array # this is the only update
while read key value; do
    array[$key]="${array[$key]}${array[$key]:+,}$value"
done < file
# print
for key in "${!array[@]}"; do echo "$key->${array[$key]}"; done

Explanation

array[$key]="${array[$key]}${array[$key]:+,}$value"

saves each $value in array[$key] separated by ,:

  • ${array[$key]} save previous value(s) (if any).
  • ${array[$key]:+,} adds a , if there's a previous value.
  • $value adds the new read value.

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

prints the values associated to each $key.


From man bash:

${parameter:+word}
If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

${!name[@]}
${!name[*]}
If name is an array variable, expands to the list of array indices (keys) assigned in name. If name is not an array, expands to 0 if name is set and null otherwise. When ‘@’ is used and the expansion appears within double quotes, each key expands to a separate word.


Example

$ cat file
1 aaaa
2 bbbb
3 cccc
4 dddd
2 eeee
2 ffff
4 gggg

$ ./script.sh 
1->aaaa
2->bbbb,eeee,ffff
3->cccc
4->dddd,gggg
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your answer! I was wondering if there is a way to print a specific value only with a specific key and the value's index, eg. "ffff"
-2

The below script is for comma separated value, you can change as per your need

awk -F "," '{OFS=","} 1 {if (a[$1]) {a[$1] = a[$1]" "$2} else {a[$1] = $2}} END {for (i in a)  { print i,a[i]}}'  input_file > Output_file

Comments