我已经编写了一个搜索功能来运行一个可能非常大的d3树,并仅筛选出与搜索模式匹配的节点。
搜索功能运行良好且速度相当快。
但是,由于在服务器上计算数据的数量和时间,我希望能够将树重置为其初始内容,如果用户清除搜索或输入另一个,而不进行其他查询。那不行。
这就是我的尝试:
首先,我看了一下[什么是最有效的方法克隆对象] [1],他们有几个建议。从Jasmine运行,我的克隆似乎工作(不确定为什么实际上)。
在我的应用中运行代码,我立即收到错误消息。确切的错误取决于我使用的代码--JSONery.extend或JSON.stringify的JSON.parse。
我怀疑这是因为每个节点d都有一个d.parent属性和d.children,因此只要你想要递归地克隆它的子节点它就进入一个循环。
然后我注意到有一个d3.layout.tree.children函数,我可以使用它来操作树。所以,我这样做而不是复制json数据,只会返回d.visible为true的节点。我的想法是,当我想要重置时,我不需要做任何事情,我只是回到基地,非过滤,孩子们。
那也不起作用 - 与搜索不匹配的子节点现在已从数据中消失,而不仅仅是图表。
更新:我已删除了我的初始示例代码,该代码无法正常工作,并且我自己给出了一个适合我的初步答案。
答案 0 :(得分:0)
不确定这是否是最优雅的方法,但它对我有用。
基本上,我只是在进行搜索之前复制对现有_nodes的引用。搜索通过树进行递归并克隆与搜索模式匹配的节点。以及具有匹配搜索模式的子节点的任何节点。 因此克隆量受匹配量的限制。 克隆代码本身非常幼稚 - 它只处理原始值,但这就是我现在所需要的。
注意:_nodes基本上是您在任何在线d3示例中看到的传入json数据。
//support an array of possible attributes for string matches...
var li_tosearch = ["label"];
//a "set" of node attributes that should not be copied
s_skip = Object.create(null);
s_skip.parent = 1;
s_skip.children = 1;
s_skip._children = 1;
s_skip._unfiltered_children = 1;
function clone_n_filter(pattern, d) {
/* recursive search by node
- a node is visible if one of its searchable attributes
matches the pattern or if one of its children/_children is
visible.
- only visible nodes get cloned
*/
if (!d || !pattern){
return;
}
var visible_child, clone, v, s;
var visible = false;
//support an array of possible attributes for string matches...
for (var i = 0; i < li_tosearch.length; ++i){
s = d[li_tosearch[i]];
if (s){
if (s.match(pattern)){
visible = true;
break;
}
}
}
var children = [], _children = [];
//if any of the children are visible then the current is visible too
if (d.children) {
for (var index = 0; index < d.children.length; ++index) {
visible_child = clone_n_filter(pattern, d.children[index]);
if (visible_child) {
children.push(visible_child);
visible = true;
}
}
}
if (d._children) {
for (var index2 = 0; index2 < d._children.length; ++index2) {
visible_child = clone_n_filter(pattern, d._children[index2]);
if (visible_child) {
_children.push(visible_child);
visible = true;
}
}
}
//not being used, but keeps track of all original children
//this is just a reference, so memory cost is low
//possible use case: expand a non-leaf node that has
//no visible children
d._unfiltered_children = d.children || d._children;
if (visible){
clone = d.constructor(); // changed
for(var key in d) {
if (key in s_skip){
continue;
}
if(d.hasOwnProperty(key)) {
v = d[key];
//given the domain, only expecting primitives...
//will address when that breaks
if (typeof(v) === 'object'){
throw("unexpected complex object:[" + typeof(v) + " " + key + "]");
}
clone[key] = v;
}
else{
//keeping track of attributes we won't be copying
s_skip[key] = 1;
}
}
clone.children = children;
clone._children = _children;
}
return clone;
}
整个事情的切入点。 重置非常快 - 它只是将_nodes设置为原始保存的引用。
_chart.search = function(pattern){
/* external entry point for calling the search*/
if (!_nodes){
return;
}
if (!pattern){
//no search pattern
if (_o_nodes){
//restore unfiltered
_nodes = _o_nodes;
}
chart.render();
return;
}
if (!_o_nodes){
//no need to clone the _nodes, just copy reference
_o_nodes = _nodes;
}
var patre = new RegExp(pattern,"gi");
_nodes = clone_n_filter(patre, _o_nodes);
chart.render();
};