|
import {Selection} from "./index.js"; |
|
import {EnterNode} from "./enter.js"; |
|
import constant from "../constant.js"; |
|
|
|
function bindIndex(parent, group, enter, update, exit, data) { |
|
var i = 0, |
|
node, |
|
groupLength = group.length, |
|
dataLength = data.length; |
|
|
|
|
|
|
|
|
|
for (; i < dataLength; ++i) { |
|
if (node = group[i]) { |
|
node.__data__ = data[i]; |
|
update[i] = node; |
|
} else { |
|
enter[i] = new EnterNode(parent, data[i]); |
|
} |
|
} |
|
|
|
|
|
for (; i < groupLength; ++i) { |
|
if (node = group[i]) { |
|
exit[i] = node; |
|
} |
|
} |
|
} |
|
|
|
function bindKey(parent, group, enter, update, exit, data, key) { |
|
var i, |
|
node, |
|
nodeByKeyValue = new Map, |
|
groupLength = group.length, |
|
dataLength = data.length, |
|
keyValues = new Array(groupLength), |
|
keyValue; |
|
|
|
|
|
|
|
for (i = 0; i < groupLength; ++i) { |
|
if (node = group[i]) { |
|
keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + ""; |
|
if (nodeByKeyValue.has(keyValue)) { |
|
exit[i] = node; |
|
} else { |
|
nodeByKeyValue.set(keyValue, node); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < dataLength; ++i) { |
|
keyValue = key.call(parent, data[i], i, data) + ""; |
|
if (node = nodeByKeyValue.get(keyValue)) { |
|
update[i] = node; |
|
node.__data__ = data[i]; |
|
nodeByKeyValue.delete(keyValue); |
|
} else { |
|
enter[i] = new EnterNode(parent, data[i]); |
|
} |
|
} |
|
|
|
|
|
for (i = 0; i < groupLength; ++i) { |
|
if ((node = group[i]) && (nodeByKeyValue.get(keyValues[i]) === node)) { |
|
exit[i] = node; |
|
} |
|
} |
|
} |
|
|
|
function datum(node) { |
|
return node.__data__; |
|
} |
|
|
|
export default function(value, key) { |
|
if (!arguments.length) return Array.from(this, datum); |
|
|
|
var bind = key ? bindKey : bindIndex, |
|
parents = this._parents, |
|
groups = this._groups; |
|
|
|
if (typeof value !== "function") value = constant(value); |
|
|
|
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { |
|
var parent = parents[j], |
|
group = groups[j], |
|
groupLength = group.length, |
|
data = arraylike(value.call(parent, parent && parent.__data__, j, parents)), |
|
dataLength = data.length, |
|
enterGroup = enter[j] = new Array(dataLength), |
|
updateGroup = update[j] = new Array(dataLength), |
|
exitGroup = exit[j] = new Array(groupLength); |
|
|
|
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); |
|
|
|
|
|
|
|
|
|
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { |
|
if (previous = enterGroup[i0]) { |
|
if (i0 >= i1) i1 = i0 + 1; |
|
while (!(next = updateGroup[i1]) && ++i1 < dataLength); |
|
previous._next = next || null; |
|
} |
|
} |
|
} |
|
|
|
update = new Selection(update, parents); |
|
update._enter = enter; |
|
update._exit = exit; |
|
return update; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function arraylike(data) { |
|
return typeof data === "object" && "length" in data |
|
? data |
|
: Array.from(data); |
|
} |
|
|