Skip to content

Instantly share code, notes, and snippets.

@devrand
Forked from mbostock/README.md
Last active January 17, 2018 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devrand/d7722c93f0e8a16299e800302f3a65f9 to your computer and use it in GitHub Desktop.
Save devrand/d7722c93f0e8a16299e800302f3a65f9 to your computer and use it in GitHub Desktop.
Underscore’s Equivalents in D3

Collections

each(array)

[1, 2, 3].forEach(function(num) { alert(num); });

each(object)

d3.values({one: 1, two: 2, three: 3}).forEach(function(num) { alert(num); });

map(array)

[1, 2, 3].map(function(num) { return num * 3; });

map(object)

d3.values({one: 1, two: 2, three: 3}).map(function(num) { return num * 3; });

reduce

var sum = [1, 2, 3].reduce(function(memo, num) { return memo + num; }, 0);

reduceRight

Given:

var list = [[0, 1], [2, 3], [4, 5]];
var flat = list.reduceRight(function(a, b) { return a.concat(b); }, []);

find

There’s no proper Vanilla equivalent of Underscore’s find method, but you can approximate it with array.filter. This is typically slower because it doesn’t stop once the first match is found:

var even = [1, 2, 3, 4, 5, 6].filter(function(num) { return num % 2 == 0; })[0];

A closer approximate is array.some, but this is less convenient because it requires the closure to set an enclosing variable:

var even; [1, 2, 3, 4, 5, 6].some(function(num) { return num % 2 == 0 && (even = num, true); });

contains

Vanilla equivalent:

[1, 2, 3].indexOf(3) >= 0;

invoke

[[5, 1, 7], [3, 2, 1]].forEach(function(array) { array.sort(); });

Note: JavaScript’s built-in array.sort sorts lexicographically rather than numerically; you almost always want to say array.sort(d3.ascending), or array.sort(function(a, b) { return a - b; }) if you know you’re sorting numbers or dates.

pluck

Given:

var stooges = [{name: "moe", age: 40}, {name: "larry", age: 50}, {name: "curly", age: 60}];
stooges.map(function(stooge) { return stooge.name; });

max

Given:

var stooges = [{name: "moe", age: 40}, {name: "larry", age: 50}, {name: "curly", age: 60}];

Vanilla equivalent. You can omit the null argument if you know that stooges is non-empty or you’d prefer a TypeError:

stooges.reduce(function(p, v) { return v.age > p.age ? v : p; }, null);

Using d3.max is not exactly equivalent because it returns the maximum value, rather than the corresponding object. Also, d3.max ignores null, undefined and NaN values. Often, this is what you want:

d3.max(stooges, function(stooge) { return stooge.age; });

min

Given:

var numbers = [10, 5, 100, 2, 1000];
numbers.reduce(function(p, v) { return Math.min(p, v); }, Infinity);

Using d3.min (not equivalent; see above regarding d3.max):

d3.min(numbers);

sortBy

[1, 2, 3, 4, 5, 6].sort(function(a, b) { return Math.sin(a) - Math.sin(b); });

In the vanilla equivalent, the comparator is invoked once per comparison rather than once per element; thus the vanilla version is potentially slower. More critically, array.sort doesn’t support nondeterministic comparators. You could fix that with a temporary array holding the mapped values, a second temporary array holding the indexes, and d3.permute:

var array = [1, 2, 3, 4, 5, 6], sin = array.map(Math.sin);
d3.permute(array, d3.range(array.length).sort(function(i, j) { return sin[i] - sin[j]; }));

groupBy

Underscore example:

_.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });

D3 equivalent:

d3.nest().key(Math.floor).map([1.3, 2.1, 2.4]);

Underscore example:

_.groupBy(["one", "two", "three"], "length");

D3 equivalent:

d3.nest().key(function(d) { return d.length; }).map(["one", "two", "three"]);

D3’s nest operator supports more than one level of grouping, and can also return nested entries that preserve order.

countBy

Underscore example:

_.countBy([1, 2, 3, 4, 5], function(num) {
  return num % 2 == 0 ? "even" : "odd";
});

D3 equivalent using nest.rollup:

d3.nest()
    .key(function(num) { return num % 2 == 0 ? "even" : "odd"; })
    .rollup(function(values) { return values.length; })
    .map([1, 2, 3, 4, 5]);

shuffle

Underscore example:

_.shuffle([1, 2, 3, 4, 5, 6]);

D3 equivalent:

d3.shuffle([1, 2, 3, 4, 5, 6]);

toArray

Underscore example:

(function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);

Vanilla equivalent:

(function(){ return [].slice.call(arguments, 1); })(1, 2, 3, 4);

size

Underscore example:

_.size({one: 1, two: 2, three: 3});

Vanilla equivalent using Object.keys:

Object.keys({one: 1, two: 2, three: 3}).length;

D3 equivalent using d3.keys:

d3.keys({one: 1, two: 2, three: 3}).length;

D3 equivalent using d3.map, if you want to perform other operations:

d3.map({one: 1, two: 2, three: 3}).keys().length;

Array Functions

first

Given:

var numbers = [5, 4, 3, 2, 1];

Vanilla equivalent when no n is specified:

numbers[0];

Vanilla equivalent when n is specified:

numbers.slice(0, n);

initial

Given:

var numbers = [5, 4, 3, 2, 1];

Vanilla equivalent when no n is specified:

numbers.slice(0, numbers.length - 1);

Vanilla equivalent when n is specified:

numbers.slice(0, numbers.length - n);

last

Given:

var numbers = [5, 4, 3, 2, 1];

Vanilla equivalent when no n is specified:

numbers[numbers.length - 1];

Vanilla equivalent when n is specified:

numbers.slice(numbers.length - n);

rest, tail, drop

Given:

var numbers = [5, 4, 3, 2, 1];

Vanilla equivalent when no n is specified:

numbers.slice(1);

Vanilla equivalent when n is specified:

numbers.slice(n);

compact

[0, 1, false, 2, '', 3].filter(function(d) { return d; });

flatten

TODO …

_.flatten([1, [2], [3, [[4]]]]);
_.flatten([1, [2], [3, [[4]]]], true);

without

TODO …

_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);

union

TODO …

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);

intersection

TODO …

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);

difference

TODO …

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);

uniq

Underscore example:

_.uniq([1, 2, 1, 3, 1, 4]);

D3 equivalent for strings:

d3.set(["1", "2", "1", "3", "1", "4"]).values();

TODO equivalent for non-string values

zip

Underscore example:

_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);

D3 equivalent:

d3.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);

object

TODO …

_.object(['moe', 'larry', 'curly'], [30, 40, 50]);

indexOf

TODO … Vanilla array.indexOf

lastIndexOf

TODO … Vanilla array.lastIndexOf

sortIndex

TODO … d3.bisect

range

Underscore example:

_.range(10);
_.range(1, 11);
_.range(0, 30, 5);
_.range(0, -10, -1);
_.range(0);

D3 equivalent:

d3.range(10);
d3.range(1, 11);
d3.range(0, 30, 5);
d3.range(0, -10, -1);
d3.range(0);

Function (uh, ahem) Functions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment