title: Implementing multidimensional associative arrays in Common Lisp
body: <p>I'm looking for the best way to implement multidimensional associative arrays in Common Lisp. I'm using SBCL.</p>
  
  <p>For example in MATLAB, I can create a struct array like this, eg. with some geographical data:</p>
  
  <pre><code>my_array.Finland.capital = 'Helsinki';
  my_array.Finland.northernmost_municipality = 'Utsjoki';
  my_array.Norway.capital = 'Oslo';
  </code></pre>
  
  <p>And then refer to it using dynamic field names:</p>
  
  <pre><code>country = 'Finland';
  city_status = 'capital';
  
  my_array.(country).(city_status)
  </code></pre>
  
  <p>would give the string <code>'Helsinki'</code> as a result.</p>
  
  <p><strong>So, what would be the best way to implement multidimensional (n-dimensional) associative arrays in Common Lisp?</strong> (as in the above example in MATLAB)</p>
  
  <p>I found a working solution based on converting the values/keys (given as strings) to numeric addresses using a hash table, and then passing those numeric address to <code>aref</code> as indices to a multidimensional array. But I wonder is there some better way to do it? I need to convert strings to other strings, strings to lists and strings to numbers (as in the example below).</p>
  
  <p>My solution is this (with an example data to convert numbers written in English to their numeric representation):</p>
  
  <p>Function <code>list-to-3d-array</code> below is a minimally modified version of Rainer Joswig's answer to <a href="https://stackoverflow.com/questions/9549568/common-lisp-convert-between-lists-and-arrays">Common Lisp: convert between lists and arrays</a> . </p>
  
  <pre><code>;;; functions.
  (defun list-to-3d-array (my-list)
    (make-array (list (length my-list)
                      (length (first my-list))
                      (length (first (first my-list))))
                :initial-contents my-list))
  
  (defun prepare-hash-table (my-hash-table factor-name factor-values-list)
    "This function stores values from 0 to n for the keys created by concatenating the factor-name and the values given in a list."
    (loop for i from 0 to (1- (length factor-values-list))
          do (setf (gethash (concatenate 'string factor-name "-" (nth i factor-values-list)) my-hash-table) i)))
  
  (defun return-value-from-assoc-array (my-array my-hash-table hundreds tens ones)
    (aref my-array
          (gethash (concatenate 'string "hundreds-" hundreds) my-hash-table)
          (gethash (concatenate 'string "tens-" tens) my-hash-table)
          (gethash (concatenate 'string "ones-" ones) my-hash-table)))
  ;;; functions end here.
  
  ;;; some example data.
  (defparameter *array-contents*
    (list 
      (list
        (list 111 112 113)
        (list 121 122 123)
        (list 131 132 133))
      (list
        (list 211 212 213)
        (list 221 222 223)
        (list 231 232 233))
      (list
        (list 311 312 313)
        (list 321 322 323)
        (list 331 332 333))))
  
  (defparameter *hundreds-in-english* (list "hundred" "twohundred" "threehundred"))
  (defparameter *tens-in-english* (list "ten" "twenty" "thirty"))
  (defparameter *ones-in-english* (list "one" "two" "three"))
  
  (defparameter *my-hash-table* (make-hash-table :test 'equal))
  ;;; example parameters end here.
  
  ;;; prepare the array.
  (defparameter *my-array* (list-to-3d-array *array-contents*))
  
  ;;;; prepare the hash table.
  (prepare-hash-table *my-hash-table* "hundreds" *hundreds-in-english*)
  (prepare-hash-table *my-hash-table* "tens" *tens-in-english*)
  (prepare-hash-table *my-hash-table* "ones" *ones-in-english*)
  </code></pre>
  
  <p>Then eg. <code>(return-value-from-assoc-array *my-array* *my-hash-table* "hundred" "ten" "two")</code>
  outputs:</p>
  
  <pre><code>112
  </code></pre>