Sunday, June 7, 2015

One Data Structure to Rule Them All

"Mark Antony, shall we give sign of battle?"
-- Shakespeare, Julius Caesar
Act V, Scene I, Line 23

Intro


For decades the battle cry of the LISP family of languages has been:

"It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures."
-- Alan J. Perlis
Epigrams on Programming, #9

What is this one data structure to rule them all?  The list?  Well yes and no.  In practice I would say, at least in Clojure, it would be the Map data structure (not to be confused with the Higher Order Function called Map).

Say we have the following data structure:


We can say that a Hobbit is made up of a mappings of name, age, family, and past time to values.

JavaScript


With ECMAScript 6 using Lodash we can use the pick and get functions to work with objects like they were Maps.


In the example above we see that with the pick we can obtain a new Map by selecting the attributes that we want.  If it is not found then it simply want be placed in the resulting Map.

If all that is wanted is one value from the Map then we can use the get function to obtain the value. 

The get function let's us provide default values.

_.get(bilbo, 'none', 'not found')
.should.equal('not found');

_.pick(bilbo, 'none')
.should.eql({});
This helps in being able to tell if the value of the attribute is null or if the attribute does not exist.

Clojure


With Clojure we can use the keyword, get, and select-keys functions (to name a few) to work with Map data structures.


In the example above we see that we can use keywords as functions and that the Map data structure its self can be used as a function.

(is (= "Bilbo" (:name bilbo)))
(is (= "Bilbo" (bilbo :name)))

Like the Lodash get function, the get function let's us provide default values.

(is (= nil (get bilbo :none)))
(is (= :not-found (get bilbo :none :not-found)))

This again helps us in being able to tell if the keyword is not found or if the value of associated with the keyword is nil.

(is (= nil (bilbo :none)))
(is (= nil (:none bilbo)))

While using the keyword as a function for an association that does not exist will return nil.

We see also that select-keys acts much like Lodash's pick, in that it returns a new Map with the keyword associations selected.

Clojure's select-keys

(is (= {:name "Bilbo"}
(select-keys bilbo [:name])))
(is (= {:name "Bilbo" :family ["Baggins" "Took"]}
(select-keys bilbo [:name :family])))

Lodash's pick

_.pick(bilbo, 'name')
.should.eql({name: 'Bilbo'});
_.pick(bilbo, ['name', 'family'])
.should.eql({name: 'Bilbo', family: ['Baggins', 'Took']});

Fin

There you have it, Map,  the one data structure to rule them all.