Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename "map" to "datum". #45

Closed
mbostock opened this issue Feb 7, 2011 · 12 comments
Closed

Rename "map" to "datum". #45

mbostock opened this issue Feb 7, 2011 · 12 comments
Milestone

Comments

@mbostock
Copy link
Member

mbostock commented Feb 7, 2011

For release 2.8:

  • data() would return a one-dimensional array of data for the first group
  • data(array) and data(function) would compute the data-join [as today]
  • data(null) would throw an error [as today]
  • datum is a new alias for map
  • datum(function) would evaluate a function for each selected element and set each associated datum [as today]
  • datum(null) would clear the bound data
  • datum(object) would assign the same data to all selected elements
  • datum() would return the bound data for the first non-null node (alias for node().__data__)

For release 3.0:

  • rename map to datum, removing alias
@jasondavies
Copy link
Contributor

So would data(null) have the same effect as data([]), or would it just remove __data__ from all nodes in the current selection?

@mbostock
Copy link
Member Author

/cc @shawnbot

@mbostock
Copy link
Member Author

See also #470.

The main design concern that is stalling this issue is the return value of data(). There are three options:

  1. Returns the first non-null element's data.
  2. Returns the first group's array of data.
  3. Returns a two-dimensional array of data, matching the selection.

I think the most consistent approach, based on the behavior of D3's other methods, is # 2. You might be thinking that # 1 is the most consistent approach, but consider: when set a value via attr, you specify the string value for a single node (perhaps a function, but you're still returning a single value); when you get a value, you get back a single value. Therefore, since the data method takes an array, or function that returns an array, it should similarly return an array of data. # 2 also has the nice behavior that d3.selectAll("p").data() returns a one-dimensional array, which is probably a common use-case.

The downside of # 2 is that there's no way to get the data from the other groups when you have a hierarchical selection, such as d3.selectAll("ul").selectAll("li").data(). However, you could get that by manually iterating over the elements, or using Array.map.

@mbostock
Copy link
Member Author

I think there's a related issue that D3 overrides the behavior of array.map, introducing its own method. That's an interesting point. I think in the 3.0 release, it might make more sense to rename map to datum. Furthermore, that would allow datum() to behave the same as # 1 in the previous comment, if you wanted to get or set the data for a single element. Hence, d3.selectAll("p").datum() would return the first non-null element's datum.

@mbostock
Copy link
Member Author

BTW, I will also point out that you can all array.map on a selection's sub-arrays. For example:

var table = d3.select("table"),
    columns = table.selectAll("thead th")[0].map(function() { return this.textContent; });

@shawnbot
Copy link

I vote 2. jQuery.data() behaves more like option 1, but I don't think it's comparable because d3's setter behaves so differently. You can get the behavior of option 3 like this, right?

d3.selectAll("ul").selectAll("li").map(function(d, i) {
  return d3.select(this).data();
});

@mbostock
Copy link
Member Author

Cool, agreement! Close; replace d3.select(this) with d3.selectAll(d), assuming you're calling array.map.

@shawnbot
Copy link

Der, yeah, thanks. Looking forward to using this!

@shawnbot
Copy link

shawnbot commented Feb 3, 2012

FWIW, I wrote a little demo of how this could work with data mapped from the DOM. There's a bit that patches d3.selection.prototype to make it work the "right" way for my purposes, but is this what you were thinking?

if (d3.version < "3.0") {
  d3.selection.prototype.data = d3.selection.prototype.map;
  d3.selection.prototype.map = function(map) {
    var out = [];
    this.each(function(d, i) {
      out.push(map.call(this, d, i));
    });
    return out;
  };
}

In other words:

  1. selection.data(function) binds data
  2. selection.data() returns an array of bound data
  3. selection.datum() returns the first bound datum
  4. selection.map(function) return an array of mapped data

Or have I gotten it completely wrong?

@mbostock
Copy link
Member Author

mbostock commented Feb 3, 2012

  1. Yes.
  2. Yes; the array of data for the first group in the selection. For a flat selection, you'll get all the data.
  3. Yes; for the first non-null node. datum(object) or datum(function) can also be used to set the data associated with each selected element, but will not compute a data-join. datum(null) can be used to clear the data associated with selected elements, as well.
  4. I'm not totally sure what we should do with map. On the one hand, I'm tempted to leave it as an alias for datum for backwards-compatibility, which would mean we could include this functionality in 2.8 rather than waiting until 3.0. On the other hand, I'm tempted to remove it completely for the sake of parsimony, which would cause it to fall back to the default array.map implementation. (We could do this in two releases, also.) A third option would be to make map an alias for data().map(function), which would return an array of mapped data. However, given that you can achieve this by calling data().map(function) already, I don't see a strong need to provide this convenience function, and I'd rather keep the API lean.

@shawnbot
Copy link

shawnbot commented Feb 3, 2012

Great, thanks. The only thing that I like about map() being part of the API is that it provides access to the DOM nodes via this, which you don't get with data().map(function). I can think of a lot of cases where it's useful to operate on data while maintaining a link back to the DOM node to which it's bound.

@mbostock
Copy link
Member Author

This appears to be fixed in 3.0.

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

No branches or pull requests

3 participants