简体   繁体   中英

ksh array over records

I need to associate values based on this table.

KEYS    VALS
----    ----
keyp    val1
keyp    val2
keyp    val3
keyp    val6
keyc    val4
keym    val4
keys    val8
keyr    val8
keyb    val5

In my ksh, when a parameter is passed that is a KEY, I want to iterate through that table so that I get all the possible VALS. For example if parm_key="keyp", my iteration would yield val1, val2, val3, val6. Psuedocode:

for iLoop in "${!KEYS2VALS[@]}"
do
    if [[ KEYS2VALS[iLoop] = $parm_key ]];then
        print "found value match=$KEYS2VALS[iLoop].VAL"
    fi
done

The output from that psuedocode should be the below if $parm_key="keyp"

found value match=val1
found value match=val2
found value match=val3
found value match=val6

Assuming you've got access to ksh93 (so we can use associative arrays) ...

NOTE: With some newer OSs /bin/ksh is ksh93 (either renamed or symlinked to ksh93); in this case ksh --version will show a string that contains 93u or 93u+

Start with a flat file that contains the desired key/value pairs:

# sample data file
$ cat key.vals
keyp val1
keyp val2
keyp val3
keyp val6
keyc val4
keym val4
keys val8
keyr val8
keyb val5

One possible ksh93 shell script:

$ cat kv
#!/bin/ksh93

# grab and verify an input key

parm_key=$1
[[ "${parm_key}" = '' ]] && printf "Usage: kv <parm_key>\n" && exit

# declare associative array 'keys2vals'

unset      keys2vals
typeset -A keys2vals

# load the key/value pairs into the associative array 'keys2vals'

while read -r key val
do
        keys2vals[${key}]="${keys2vals[${key}]} ${val}"
done < key.vals

# for debug purposes only: display array

printf "All key/value pairs:\n\n"
for key in ${!keys2vals[@]}
do
        echo "key=${key} : value=${keys2vals[${key}]}"
done
echo ""

# print the values associated with ${parm_key}

printf "All values for key = ${parm_key}:\n\n"
for val in ${keys2vals[${parm_key}]}
do
        echo "found value match=${val}"
done

The script in action:

$ kv
Usage: kv <parm_key>

$ kv keyp
All key/value pairs:

key=keyb : value= val5
key=keyc : value= val4
key=keym : value= val4
key=keyp : value= val1 val2 val3 val6
key=keyr : value= val8
key=keys : value= val8

All values for key = keyp:

found value match=val1
found value match=val2
found value match=val3
found value match=val6

# commenting out the debug loop ...

$ kv keyb
All values for key = keyb:

found value match=val5

$ kv keyc
All values for key = keyc:

found value match=val4

$ kv keym
All values for key = keym:

found value match=val4

$ kv keyr
All values for key = keyr:

found value match=val8

$ kv keys
All values for key = keys:

found value match=val8

I managed to get 2-dimensional associative arrays working for this example:

First, let's put that data into a file

$ cat table.txt
KEYS    VALS
----    ----
keyp    val1
keyp    val2
keyp    val3
keyp    val6
keyc    val4
keym    val4
keys    val8
keyr    val8
keyb    val5

We'll use an associative array named "table"

typeset -A table

Now, let's populate the array. The tricky bit here is that we need to declare that each element of the array is itself an associative array. We need to do this when we encounter a key that is not in "table":

i=0
sed '1,2d' table.txt | while read -r key value; do
    [[ -z "${table[$key]+not set}" ]] && typeset -A table[$key]
    table[$key][$value]=$((++i))
done

Now, let's see what we have in the table array

for key in "${!table[@]}"; do 
    for subkey in "${!table[$key][@]}"; do 
        printf "%s\t%s\t%s\n" "$key" "$subkey" "${table[$key][$subkey]}"
    done
done
keyb    0   
keyb    val5    9
keyc    0   
keyc    val4    5
keym    0   
keym    val4    6
keyp    0   
keyp    val1    1
keyp    val2    2
keyp    val3    3
keyp    val6    4
keyr    0   
keyr    val8    8
keys    0   
keys    val8    7

All is well, except that every top-level key also has a subkey "0". That looks odd, but I think that's how variables are implemented in ksh. Even simple scalar variables can be referenced as an array with index 0:

$ foo=bar
$ echo "$foo"
bar
$ echo "${foo[0]}"
bar
$ printf "%s\n" "${!foo[@]}"
0

To the heart of the question: retrieve the values for a particular key:

parm_key="keyp"
typeset -a values
for val in "${!table[$parm_key][@]}"; do
    [[ $val = "0" ]] || values+=("$val")
done
printf "%s\n" "${values[@]}"
val1
val2
val3
val6

I just realized this is quite overcomplicated. We don't need an associative array of associative arrays. we want an associative array of indexed arrays:

$ unset table
$ typeset -A table
$ sed '1,2d' table.txt | while read -r key value; do table[$key]+=("$value"); done
$ printf "%s\n" "${!table[@]}"
keyb
keyc
keym
keyp
keyr
keys
$ for key in "${!table[@]}"; do for val in "${table[$key][@]}"; do printf "%s\t%s\n" "$key" "$val"; done; done
keyb    val5
keyc    val4
keym    val4
keyp    val1
keyp    val2
keyp    val3
keyp    val6
keyr    val8
keys    val8
$ printf "%s\n" "${table[$parm_key][@]}"
val1
val2
val3
val6

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM