5 Just Enough JS
Basics: IDVW, pp. 36-52
Before JavaScript ES6, the ‘var’ keyword was used to declare a variable. Variables declared using the ‘var’ keyword are either globally or functionally scoped, they do not support block-level scope. Therefore, in JavaScript ES6, the ‘let’ keyword and ‘const’ keyword were introduced.
The ‘let’ keyword deals with a block scope. It can be reassigned but cannot be redeclared.
The ‘const’ keyword is also blocked scoped. It cannot be reassigned and cannot be redeclared.
As a general rule, you should always declare variables with ‘const’, if you realize that the value of the variable needs to change, go back and change it to ‘let’.
For more detailed information regaring ‘var’, ‘let’ and ‘const’, please see Difference between var, let, and const keyword in JavaScript
objects, arrays, arrays of objects, functions (and other things)
5.1 Arrays of arrays
Open the JavaScript Console
// try me in the Console
const array_dataset = [[100, 75, 30], [200, 125, 20]];
.select("svg#arrays")
d3.selectAll("circle")
.data(array_dataset)
.enter()
.append("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
.attr("r", d => d[2])
.attr("fill", "red");
5.2 Arrays of objects
// Try me in the Console
const object_dataset = [
cx: 100, cy: 150, fill: `red`},
{cx: 200, cy: 100, fill: `blue`}
{;
]
.select("svg#objects")
d3.selectAll("circle")
.data(object_dataset)
.enter()
.append("circle")
.attr("cx", d => d.cx)
.attr("cy", d => d.cy)
.attr("r", "30")
.attr("fill", d => d.fill);
See also: JavaScript Array of Objects Tutorial
5.3 .map()
What’s the issue?
In R many operations are vectorized:
sqrt(3)
## R output ## [1] 1.732051
c(3, 5, 7)
x <-sqrt(x)
## R output ## [1] 1.732051 2.236068 2.645751
Not so in JavaScript:
Math.sqrt(3); // Try me in the Console
const x = [3, 5, 7]; // Try me in the Console
Math.sqrt(x); // Doesn't work...
5.3.1 Simple arrays
Use .map()
to operate on each array element separately. The concept is similar to lapply()
or purrr::map()
, but unlike in R, it’s needed for simple arrays.
R
c(3, 5, 7)
x <-sqrt(x)
## R output ## [1] 1.732051 2.236068 2.645751
JavaScript
Do something to every element of a simple array:
// take the square root of each element
const x = [3, 5, 7]; // try me
.map(Math.sqrt); x
// multiply each element by 3
4, 10, 12].map(d => d*3); // try me [
// multiply each element by 3
4, 10, 12].map(function(d) {return d*3;}); // try me [
// multiply each element by its index
10, 20, 30, 40].map((d, i) => d*i); // try me [
R: Sum two arrays
# sum two arrays
1:3
x <- 4:6
y <-+ y x
## R output ## [1] 5 7 9
JavaScript: Sum two arrays
// sum two arrays
const x = [1, 2, 3];
const y = [4, 5, 6];
+ y // try me... what went wrong? x
// sum two arrays
const x = [1, 2, 3];
const y = [4, 5, 6];
.map((d, i) => d + y[i]); // try me x
5.3.2 Arrays of arrays
Do something to the first item of every element of a nested array:
1, 2], [3, 4]].map(d => Math.sqrt(d[0])) // try me [[
Sum up all items in each element of the array:
1, 2, 3], [4, 5, 6]].map(d => d[0] + d[1] + d[2]); // try me [[
Created a nested array out of a simple array:
10, 20, 30].map(d => [d, Math.pow(d, 2)]); [
5.4 D3 sorting
Use d3.sort()
rather than plain JavaScript options.
const y = [3, 1, 5, 12, 7]; // try me
.sort(y); d3
5.5 D3 statistics
D3 brings us back to familiar ground with functions that take an array and return a single value. Here are D3 functions with the same names and behavior as their R equivalents:
R | D3 |
---|---|
min(x) |
d3.min(x) |
max(x) |
d3.max(x) |
sum(x) |
d3.sum(x) |
mean(x) |
d3.mean(x) |
median(x) |
d3.median(x) |
A few with different names:
R | D3 |
---|---|
range(x) |
d3.extent(x) |
var(x) |
d3.variance(x) |
sd(x) |
d3.deviation(x) |
d3.quantile()
takes a single value for p
, not an array as in R. (In earlier versions of D3 it was necessary to sort the array before finding quantiles, but this is no longer the case.)
R | D3 |
---|---|
quantile(x) |
d3.quantile(x, p) |
Thus for a single quantile we have:
const x = [12, 34, 1, 43, 90, 72]; // try me
.quantile(x, .25); d3
5.6 D3 + .map()
D3 statistics functions combined with .map()
can be helpful in a variety of situations.
Vectorizing a parameter, for example to mimic quantile(x)
in R:
R
c(1, 12, 34, 43, 72, 90);
x <-quantile(x)
## R output ## 0% 25% 50% 75% 100%
## R output ## 1.00 17.50 38.50 64.75 90.00
JavaScript
const x = [1, 12, 34, 43, 72, 90]; // try me
0, .25, .5, .75, 1].map(p => d3.quantile(x, p)); [
Sum up the first item of all elements in an array of arrays:
R
list(c(100, 200, 40), c(300, 150, 20))
l <-sum(purrr::map_dbl(l, ~.x[1]))
## R output ## [1] 400
JavaScript
const dataset = [[100, 200, 40], [300, 150, 20]]; // try me
.sum(dataset.map(d => d[0])); d3
Sum up all items in each array to create a simple array:
R
list(c(100, 200, 40), c(300, 150, 20))
l <-::map_dbl(l, ~sum(.x)) purrr
## R output ## [1] 340 470
JavaScript
const dataset = [[100, 200, 40], [300, 150, 20]]; // try me
.map(d => d3.sum(d)); dataset