d3.each() does begin at index 0. What you're seeing in your code is the expected behaviour, given what you have in your code.
The issue here is simple: there is a <body> element in the page, of course. Your data array has 5 elements, and one of them is being bound to the <body>.
Let's show this. Have a look at the size of the "enter" selection:
data = [0, 1, 2, 3, 4]
var foo = d3.select("body")
.data(data)
.enter();
console.log("Size of enter selection: " + foo.size())
<script src="https://d3js.org/d3.v4.js"></script>
We can also show that the first element in the array was bound to the <body>:
data = [0, 1, 2, 3, 4]
var foo = d3.select("body")
.data(data)
.enter();
console.log("Data of body: " + d3.select("body").data())
<script src="https://d3js.org/d3.v4.js"></script>
Yet another way for showing this is using the third argument (technically speaking, parameter), which is the current group:
data = [0, 1, 2, 3, 4]
d3.select("body")
.data(data)
.enter()
.each((d, i, p) =>
// ^---- this is the third argument
console.log(p)
)
Here I cannot provide a working Stack snippet, because it crashes if we try to log a D3 selection. But the result will be this:
[undefined × 1, EnterNode, EnterNode, EnterNode, EnterNode]
That undefined is the "update" selection (the body), and the 4 EnterNodes are the "enter" selection. And that brings us to the explanation of why each() behaves that way in your code.
If you have a look at the source code...
function(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) callback.call(node, node.__data__, i, group);
}
}
return this;
}
You're gonna see that it works comparing the nodes to the group, and your group contains both the "update" selection and the "enter" selection. The update selection corresponds to index 0 and the enter selection corresponds to indices 1, 2, 3, and 4.
Solution:
This is what you want, pay attention to selectAll and null:
data = [0, 1, 2, 3, 4]
d3.select("body")
.selectAll(null)
.data(data)
.enter()
.each((d, i) =>
console.log(i, d)
)
<script src="https://d3js.org/d3.v4.js"></script>
Thus, as you can see, selecting null assure us that our "enter" selection always have all the elements in the data array.
Bonus: select and selectAll behave differently. Most people think that the only difference is that the former selects only 1 element and the latter selects all the elements. But there are more subtle differences. Have a look at this table:
| Method |
select() |
selectAll() |
| Selection |
selects the first element that matches the selector string |
selects all elements that match the selector string |
| Grouping |
Does not affect grouping |
Affects grouping |
| Data propagation |
Propagates data |
Doesn't propagate data |