Have you ever missed any of Python’s neat packages like defaultdict when coding in JavaScript? Unfortunately this reflection functionality wasn’t given in JavaScript.

However now thanks to the Proxy API we can intercept property lookups and build a native defaultdict in JS. Proxies are part of the ECMAScript 6 standard and are already implemented in Edge, Firefox and Chrome (>= 49) (more details).

Starting simple

I have built a pure defaultdict JS/Node package for which I will show why it’s so pleasant to finally have reflection in JavaScript. In contrast to other implementations, one doesn’t need to use any special getter and it really can be used like it’s Python “brother”. Detailed instructions for its Browsers or Node.Js use are provided on github.

For a start let’s consider this very simply example:

var d = defaultdict(0);
d.a++; 
d.b++;
console.log(d);
// { a: 1, b: 1 }

And its similar Python version:

d = defaultdict(int)
d["a"] += 1
d["b"] += 1
print(d)
# defaultdict(<class 'int'>, {'b': 1, 'a': 1})

Coding with Defaultdict is fun!

Our first example wasn’t very fascinating as we need for the direct assignment (d.a=1) the same amount of characters (if whitespaces are removed).

However coding with defaultdict is a lot of fun! Let’s consider building a 2-kmers counting matrix.

Traditionally we would have written this in JavaScript:

var seq = "AGAGACGAG";
var d = {};
for(var i=0; i < seq.length -1;i++){
	if(typeof d[seq[i]] === "undefined"){
		d[seq[i]] = {}
	}
	if(typeof d[seq[i]][seq[i+1]] === "undefined"){
		d[seq[i]][seq[i+1]] = 0;
	}
	d[seq[i]][seq[i+1]]++;
}
console.log(d);
// { A: { G: 3, C: 1 }, G: { A: 3 }, C: { G: 1 } }

Now let’s have a look at this neat version with defaultdict. Note that we can save both ifs with which we have to check for the existence and initialization.

var seq = "AGAGACGAG";
var d = defaultdict(()=>defaultdict(0));
for(var i=0; i < seq.length -1;i++){
	d[seq[i]][seq[i+1]]++;
}
console.log(d);
// { A: { G: 3, C: 1 }, G: { A: 3 }, C: { G: 1 } }

Even the Python code is a bit longer!

seq = "AGAGACGAG";
d = defaultdict(lambda: defaultdict(int))
for i in range(len(seq) - 1):
    d[seq[i]][seq[i+1]] += 1
print({k:dict(v) for k,v in d.items()})
# {'G': {'A': 3}, 'C': {'G': 1}, 'A': {'G': 3, 'C': 1}}

Where to go

Proxies are already implemented in Edge, Firefox and Chrome (>=49) and for Node.js one can shim them.

↪ Have fun coding or check out the live demo!