4

I have a list of triplets (3-tuple) like this:

Hank   01.09.1999  Syracuse
Betty  10.30.1987  Sacramento
...

the "key" should be the first column, the person's name. What is a good elisp data structure way to represent this data? I know alist and hash are key-value oriented, here I have three objects. I want to read a text file with the raw (tab delimited) data and write it to a file in the elisp data structure. As a beginner, I'm thinking

((Hank (01.09.1999 . Syracuse)) (Betty (10.30.1987 . Sacramento)) ... )

where I cons the birth date and the birth city, then cons to dotted pair to the name, but is there a better way? I guess there is no "dotted triplet" i.e., (a . b . c)? In general, I could use some examples of live elisp code where code and data are blended together, too.

1
  • 1
    There isn't really a thing like a "good" data structure, so people go for the most convenient one. Which usually happens to be a list...
    – wasamasa
    Commented Oct 4, 2015 at 18:33

4 Answers 4

9

I'd suggest using a cl-struct. Here's an example:

(cl-defstruct record name date city)

(make-record :name "Hank" :date "01.09.1999" :city "Syracuse")
;; [cl-struct-record "Hank" "01.09.1999" "Syracuse"]

Here are some reasons to prefer structs to all kinds of lists.

  1. since the number of fields is known in advance, you can take advantage of using arrays as underlying data-structure, this makes field access time minimal, as compared to lists.
  2. cl-defstruct also creates special functions for working with structs, using these allows you to make your code independent of data layout. In the future, if you wanted to add a new field to the struct, or to remove one, you wouldn't need to modify the code which doesn't work with the field which shouldn't be affected by the change.
  3. You can add documentation to the struct and its fields.
  4. Structs can be used with EIEIO (the Elisp Object system), meaning you can specialize methods on structs (you get type-checking free).
6

A list might work best. There's no such thing as a dotted triplet.

It's also possible to use defstruct, if you like:

(cl-defstruct tuple
  name
  date
  city)
(setq x (make-tuple
         :name "Hank"
         :date "01.09.1999"
         :city "Syracuse"))
;; =>
;; [cl-struct-tuple
;;  "Hank"
;;  "01.09.1999"
;;  "Syracuse"]
(tuple-name x)
;; => "Hank"
(tuple-date x)
;; => "01.09.1999"
2
  • Yeah, I was 24 seconds too late :)
    – wvxvw
    Commented Oct 4, 2015 at 18:30
  • It's funny how two people can be writing the same thing at once:)
    – abo-abo
    Commented Oct 4, 2015 at 18:31
5

What data structure you use depends on how you will be using it, in particular, how you will be accessing and setting its parts.

If you have no special needs in this regard, consider starting with an alist. In that case, there is typically no reason to prefer an element such as (Hank (0.1.09.1999 . Syracuse)) to an element such as (Hank 0.1.09.1999 . Syracuse) (or even just (Hank 0.1.09.1999 Syracuse). All of these associate the key Hank with 0.1.09.1999 and Syracuse, but in different ways.

If you are not already, become familiar with how to access elements of a list (a cons, really), starting with car and cdr, but including also nth and nthcdr. (An alist is just a list whose elements are conses.)

Once you know how to grab the content you want from an alist element, and you know how to get the alist element you want, you can consider, for convenience and readability, writing accessor macros.

For example, if your alist elements look like (Hank 0.1.09.1999 Syracuse) then here are some accessor macros:

(defmacro name (triplet)
  `(car ,triplet))

(defmacro date (triplet)
  `(cadr ,triplet))

(defmacro location (triplet)
  `(car (cddr ,triplet)))

Note that you get this kind of thing (and much more) if you use defstruct. But note too that an alist can have multiple entries that have the same key (e.g. multiple entries with key Hank) -- this is undefined for defstruct (in some implementations it raises an error).

For most purposes (e.g. access by assq or assoc), the first matching entry shadows the others. But you can, if you want, create and use alists that have entries with identical keys but different values, and this can be useful, depending on the application.

Really, to repeat, what kind of structure you use depends on how you use it, including how easily you want to be able to add, delete, or modify entries, whether the structure needs to be ordered. (Think of the differences in use between vectors, lists, and hash tables, for instance.)

1

If you have a lot of key/value pairs, a hash table is an option to consider (if not so many, an alist is fine). If you like, you can use mini-alists or mini-plists as the hash table's values:

(let ((hash (make-hash-table :test #'equal)))
  ;; examples of adding a text-based key and value combination
  (puthash "Hank"  (list "01.09.1999" "Syracuse") hash)
  (puthash "Betty" '((:bday "10.30.1987") (:city "Sacramento")) hash)
  (puthash "Tom"  '(:bday "01.02.2000" :city "Chicago") hash)
  ;; examples of retrieving a value
  (car (gethash "Hank" hash))                  ; => "01.09.1999"
  (assoc :bday (gethash "Betty" hash))         ; => (:bday "10.30.1987")
  (cadr (assoc :bday (gethash "Betty" hash)))  ; => "10.30.1987" 
  (plist-get (gethash "Tom" hash) :city))      ; => "Chicago"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.