Sometimes I do a code kata at codewars.com. That is a fun way to solve computer science related problems, learn on the way to solve them and especially learn from the solutions of others.
Today I completed the kata “Make a spanning tree” using Javascript. I occasionally use Javascript to write an event handler or so but I don’t have much experience in “modern” Javascript. Here is what I learnt from looking at the solutions of others.
Destructuring
I know this from my Scala class and Clojure.
You can assign array elements to variables:
1 2 3 4 5 6 7 8 9 10 11 12 |
var a, b, rest; [a, b] = [10, 20]; console.log(a); // expected output: 10 console.log(b); // expected output: 20 [a, b, ...rest] = [10, 20, 30, 40, 50]; console.log(rest); // expected output: [30,40,50] |
so “…rest” is assign the rest of the array.
This is nice syntactic sugar also when working with nested arrays. Eg when “edges” is an array of pairs:
1 2 |
// sort edges by weight edges.sort(([edge_a, a], [edge_b, b]) => a - b); |
There is object destructuring:
1 2 3 4 5 |
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true |
and even assigning to new variable names
1 2 3 4 5 |
var o = {p: 42, q: true}; var {p: foo, q: bar} = o; console.log(foo); // 42 console.log(bar); // true |
See MDN web docs for more.
Spread operator to create an array using an array literal
Using an array literal to create an array from two other arrays:
1 2 3 4 |
const sets = {}; //... // new array with sets[a] elements and sets[b] elements const set = [...sets[a], ...sets[b]]; |
Objects are associative arrays (aka Maps)
Although I already knew this, kind of, this refreshes my JS
knowledge.
First, you can add properties to Objects without declaring them in
the first place:
1 2 3 |
let obj = {}; // anonymous object obj.height=2; // create new property "heigth" and assign value console.log(obj.height); // 2 |
Second, instead of the dot-notation you can use array index
notation using the property name as the index:
1 2 3 |
let ojb = {}; obj['height'] = 2; console.log(obj['height']); // 2 |
One solution uses this in order to save the weighted edges in an
object just like i did in the proper Map object:
1 2 3 4 |
let set = {}; edges.filter(e => e[0][1] !== e[0][0]).forEach(e => { if (!set[e[0]] || minOrMaxFunc(set[e[0]], e[1])>00) { set[e[0]] = e[1]; } }); |
Third, methods are kind of properties, too. In the same solution,
“minOrMaxFunc” is cleverly choosen (“minOrMax” argument is either
“min” or “max”):
1 2 3 4 |
function makeSpanningTree(edges, minOrMax) { let minOrMaxFunc = { min: (a, b) => a - b, max: (a, b) => b - a }[minOrMax]; // ... } |
it creates an objects with two methods: “min” and “max” and then
accesses the one that is given in the argument. If “minOrMax” is
“min”, a reference of the “min” method is returned.
Strings are arrays
Destructuring works with strings:
1 2 3 |
let [a,b] = 'ABC'; console.log(a); // "A" console.log(b); // "B" |
and you can index strings:
1 2 |
const s = "ABC"; s[1]; // "B" |
“var” vs. “let”
Of course, the solutions written in “modern” JS use “let” and
“const” all over the place. I just reassured myself about the
difference between let and var:
First, variables declared in a block using “var” are visible
outside that block and are “known” before being declared:
1 2 3 4 5 |
function f() { console.log(v); // undefined { var v = 3; } console.log(v); // 3 } |
a block might be a for-loop.
Variables declared using let are not visible outside the block and
are not “known” before declared:
1 2 3 4 5 |
function f() { console.log(v); // Reference error { let v = 3; } console.log(v); // Reference error } |
Third, you might not redeclare a variable using let:
1 2 3 4 |
var a = 0; var a = 1; // OK let b = 0; let b = 1; // not OK |
So basically, “let” is a sane way to declare variables.