Translate

Image of Linux Kernel Development (3rd Edition)
Image of Modern Operating Systems (3rd Edition)
Image of XSLT 2.0 and XPath 2.0 Programmer's Reference (Programmer to Programmer)
Image of Operating System Concepts

Sort Korn Shell 93 Associative Arrays

Both ksh93 and bash V4 support associative arrays. However neither provide any built-in mechanism to sort associative arrays. Recently I came across the following problem. Given a list of IP addresses such as the following:

192.168.1.10
192.168.1.10
192.168.1.10
192.168.1.11
192.168.1.15
192.168.1.15
192.168.1.20
192.168.1.22


how can one produce a group by summary report similar to the following:

192.168.1.10 : 3
192.168.1.15 : 2
192.168.1.20 : 1
192.168.1.11 : 1
192.168.1.22 : 1


The report counts the occurrences of each IP address in the input list, and outputs the count in descending order. Note it does not try to sort the IP addresses where the count is the same.

With regular non-associative arrays you could use a simple bubble sort to sort the array elements but this approach does not work for associative arrays. Why not? Think about it! The answer lies in the fact that we cannot assign one associative array element to another associative array element as we can with regular arrays.

Here is the solution I came up with:

#!/bin/ksh93

typeset -A count

# create an associative array
while read ip
do
    (( count[$ip]++ ))
done < infile

# sort print the associative array
while (( 1 ))
do
   [[ ${#count[@]} == 0 ]] && break;
   k=(${!count[@]})
   for j in ${!count[@]}
   do
      (( ${count[$j]} > ${count[$k]} )) && k=$j
   done
   echo "$k : ${count[$k]}"
   unset count[$k]
done


It uses an associate array count to count the number of occurrences of each of the IP addresses. After reading the input file infile the contents of the associative array is as follows:

192.168.1.15 : 2
192.168.1.20 : 1
192.168.1.10 : 3
192.168.1.11 : 1
192.168.1.22 : 1


if you want to see this for yourself, add the following code to the script just above the sort print routine comment:

echo "Unsorted"
for i in ${!count[@]}
do
    echo "$i : ${count[$i]}"
done
echo "Sorted"


Turning now to the sort print routine, the first thing you should notice is that it is an infinite while loop. The construct (( 1 )) is used to create the infinite loop. While the number of elements in the associative array count is greater than zero, this loop continues to execute. The loop is existed via a break statement when the number of elements in the associative array reaches zero. The construct ${#count[@]} returns the number of elements in the associative array. Then the shell selects the first item in the associative array. You cannot use $count[0] to do this as this construct only works for regular arrays and not for associative arrays. Instead you need to use the (${!count[@]}) to assign the first value from the list of associative array keys to the variable k. If you use k=${!count[@]} it assigns a list of all the associative array keys to k. For example

k=${!count[@]}
echo $k
k=(${!count[@]})
echo $k


outputs

192.168.1.15 192.168.1.20 192.168.1.10 192.168.1.11 192.168.1.22
192.168.1.15


The rest of thesort print routine should be easy to understand. It simply finds the largest value and outputs the IP address and its value. It then removes that particular IP address from the associative array using the unset command and repeats the loop until the associative array count is 0.
 

1 comment to Sort Korn Shell 93 Associative Arrays

  • “The construct (( 1 )) is used to create the infinite loop. While the number of elements in the associative array count is greater than zero, this loop continues to execute. The loop is existed via a break statement when the number of elements in the associative array reaches zero. The construct ${#count[@]} returns the number of elements in the associative array. Then the shell selects the first item in the associative array. You cannot use $count[0] to do this as this construct only works for regular arrays and not for associative arrays. Instead you need to use the (${!count[@]}) to assign the first value from the list of associative array keys to the variable k. If you use k=${!count[@]} it assigns a list of all the associative array keys to k.”
    Can more?