Skip to content

Commit

Permalink
Make .data() binding work on iterable protocol
Browse files Browse the repository at this point in the history
The current data binding implementation relies on random array-style
access, but only ever accesses data sequentially. Some data structures
(such as linked lists) cannot easily expose random access. Moreover,
since this specifically uses bracket indexing, it only accepts `Array`s
or first-class array-like objects. Making a custom data structure
indexable this way requires either an expensive `Proxy` (indexes have to
be round-tripped from integers to strings on every access) or else
requires the custom class to add explicit integer properties for each
contained item; this adds memory bloat linear in the size of the data
structure itself and also requires unnecessary bookkeeping.

If random access were required, it might be reasonable to require some
cheaply-implemented but customizable accessor method (see, for example
Array.at()). However, since this is not the case, it makes more sense to
instead only require that input data types be iterable. This opens up
efficient implementations for structures such as singly-linked lists and
other purely applicative structures. Backward compatibility can be
maintained by adding a cheap wrapper to implement iteration in terms of
sequential indexing.
  • Loading branch information
bsidhom committed Dec 18, 2024
1 parent d8ea800 commit 4223e4e
Showing 1 changed file with 11 additions and 10 deletions.
21 changes: 11 additions & 10 deletions src/selection/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import constant from "../constant.js";
function bindIndex(parent, group, enter, update, exit, data) {
var i = 0,
node,
groupLength = group.length,
dataLength = data.length;
groupLength = group.length;

// Put any non-null nodes that fit into update.
// Put any null nodes into enter.
// Put any remaining data into enter.
for (; i < dataLength; ++i) {
for (const datum of data) {
if (node = group[i]) {
node.__data__ = data[i];
node.__data__ = datum;
update[i] = node;
} else {
enter[i] = new EnterNode(parent, data[i]);
enter[i] = new EnterNode(parent, datum);
}
i += 1;
}

// Put any non-null nodes that don’t fit into exit.
Expand All @@ -33,7 +33,6 @@ function bindKey(parent, group, enter, update, exit, data, key) {
node,
nodeByKeyValue = new Map,
groupLength = group.length,
dataLength = data.length,
keyValues = new Array(groupLength),
keyValue;

Expand All @@ -53,15 +52,17 @@ function bindKey(parent, group, enter, update, exit, data, key) {
// Compute the key for each datum.
// If there a node associated with this key, join and add it to update.
// If there is not (or the key is a duplicate), add it to enter.
for (i = 0; i < dataLength; ++i) {
keyValue = key.call(parent, data[i], i, data) + "";
i = 0;
for (const datum of data) {
keyValue = key.call(parent, datum, i, data) + "";
if (node = nodeByKeyValue.get(keyValue)) {
update[i] = node;
node.__data__ = data[i];
node.__data__ = datum;
nodeByKeyValue.delete(keyValue);
} else {
enter[i] = new EnterNode(parent, data[i]);
enter[i] = new EnterNode(parent, datum);
}
i += 1;
}

// Add any remaining nodes that were not bound to data to exit.
Expand Down

0 comments on commit 4223e4e

Please sign in to comment.