Iterating over an array of objects

3,742 views
Skip to first unread message

Iain

unread,
May 11, 2011, 4:42:45 AM5/11/11
to d3-js
Hi there,

I have an array of objects, from which I wish to draw a simple bar
graph. Each object has two attributes: lang and post_count. lang
contains the (nominal) data to plot on the x axis; post_count contains
the data to plot on the y axis.

My question: Is there a simple way to iterate over the array of
objects, for each object returning only the value of the attribute I
am interested in? I wish to use the resulting array of values to
construct x and y axis scales, for example:

// x axis scale
var x = d3.scale.ordinal().domain(all_langs).rangeBands([0, 200]);

// y axis scale
var y = d3.scale.linear().domain(all_post_counts).range([0, 854400]);

I feel the answer lies in d3 methods such as keys, values and nest.
However, I'm learning JavaScript as I go; the method names alone don't
provide me with enough to get started. A similar question was asked
previously, although the response didn't quite go far enough*. I would
really appreciate a leg-up!

Thanks in advance,

ID

* http://groups.google.com/group/d3-js/browse_thread/thread/e48da253715a8b5e/314d229b225d00bb?lnk=gst&q=d3.nest%28%29#314d229b225d00bb

Jason Davies

unread,
May 11, 2011, 5:14:46 AM5/11/11
to d3...@googlegroups.com
On Wed, May 11, 2011 at 01:42:45AM -0700, Iain wrote:
> My question: Is there a simple way to iterate over the array of
> objects, for each object returning only the value of the attribute I
> am interested in? I wish to use the resulting array of values to
> construct x and y axis scales, for example:
>
> // x axis scale
> var x = d3.scale.ordinal().domain(all_langs).rangeBands([0, 200]);
>
> // y axis scale
> var y = d3.scale.linear().domain(all_post_counts).range([0, 854400]);

Sounds like you want to use JavaScript's built-in .map() function for
arrays. For example:

var x = d3.scale.ordinal()
.domain(array.map(function(d) { return d.lang; }))
.rangeBands([0, 200]);

Note that map is a fairly recent addition to the standard, so you may
need to add a workaround for older browsers e.g. see:
<https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map>
for an example of this.

Hope that helps,
--
Jason Davies, http://www.jasondavies.com/

Iain

unread,
May 11, 2011, 10:42:01 AM5/11/11
to d3-js
Wonderful! Thanks for your help, and for the link to the
documentation.

I see from the examples and the list of methods on the main d3 website
that d3.nest() also has a map() method. By looking at the source, I
believe d3.nest().map() is different to JavaScript's built-in map().
Would it be reasonable to assume that the former would work with older
browsers, whereas the latter would not?

For example, I believe the following two examples are functionally
equivalent. However, if my assumption is correct then the first
example would be better than the second. (Note "data" is the original
array of objects, where each object has "lang" and "post_id"
attributes.)

var keys = d3.nest().map(data).map(function(d) { return d.lang; });

var keys = data.map(function(d) { return d.lang; });

ID

Mike Bostock

unread,
May 11, 2011, 12:16:51 PM5/11/11
to d3...@googlegroups.com
> My question: Is there a simple way to iterate over the array of
> objects, for each object returning only the value of the attribute I
> am interested in? I wish to use the resulting array of values to
> construct x and y axis scales, for example:

You may want to use D3's built-in min and max functions, which allow
you to specify an accessor. This is much like using JavaScript's
array.map method, but it's a bit more direct if you just want to
compute the extent of a set of values. For example, to use the domain
[0, max], where *max* is the maximum post count, you might say:

var y = d3.scale.linear()
.domain([0, d3.max(objects, function(d) { return d.post_count; })])
.range([0, 854400]);

In the case of `lang`, which is an ordinal variable, it makes the most
sense to use map. Jason's example has that covered!

Mike

Mike Bostock

unread,
May 11, 2011, 12:20:13 PM5/11/11
to d3...@googlegroups.com
> I see from the examples and the list of methods on the main d3 website
> that d3.nest() also has a map() method. By looking at the source, I
> believe d3.nest().map() is different to JavaScript's built-in map().

These methods are completely different. It's named `map` because it
returns a map (an associated array from keys to values). It's not a
mapping operation on an array.

The `nest` operator is designed to group tabular data (an array of
objects, each with different attributes—equivalently, rows & colums)
into a hierarchical structure. For example, you might have a set of
samples for time-series data, where each sample is a post count for a
particular time, labeled with an associated language. If you nest
these samples by language, then you get an array of arrays of samples.
For example, this two-dimensional array can then be used with the
d3.layout.stack operator, mapping each language to a layer in a
streamgraph.

Mike

Iain

unread,
May 11, 2011, 4:30:59 PM5/11/11
to d3-js
Fantastic! Thanks for the assistance. The accessor with the max and
min functions does the trick, although I've noticed that I've had to
use JavaScripts' parseInt(). For example:

// y axis scale
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return
parseInt(d.post_count); })])
.range([0, h]);

Thanks also for highlighting the different uses of map(). Really
helpful.

ID
Reply all
Reply to author
Forward
0 new messages