Javascript 将数据排序到树中
我有以下数据:Javascript 将数据排序到树中,javascript,sorting,Javascript,Sorting,我有以下数据: var data = [ { index : 1, sort : 10, parent : 0 }, { index : 2, sort : 7, parent : 0 }, { index : 3, sort : 15, parent : 1 }, { index : 4, sort : 4, parent : 0 }, { index : 5, sort : 13, parent : 1 }, { index : 6, sort
var data = [
{ index : 1, sort : 10, parent : 0 },
{ index : 2, sort : 7, parent : 0 },
{ index : 3, sort : 15, parent : 1 },
{ index : 4, sort : 4, parent : 0 },
{ index : 5, sort : 13, parent : 1 },
{ index : 6, sort : 20, parent : 5 },
{ index : 7, sort : 2, parent : 8 },
{ index : 8, sort : 6, parent : 5 },
];
如何根据父ID和排序值对其进行有效排序,以便最终得到:
var data = [
{ index : 4, sort : 4, parent : 0 },
{ index : 2, sort : 7, parent : 0 },
{ index : 1, sort : 10, parent : 0 },
{ index : 5, sort : 13, parent : 1 },
{ index : 8, sort : 6, parent : 5 },
{ index : 7, sort : 2, parent : 8 },
{ index : 6, sort : 20, parent : 5 },
{ index : 3, sort : 15, parent : 1 },
];
这是一个树形结构。每个元素后面紧跟着任何子元素,同一分支上的所有元素都按排序值排序
我能想到的最好办法是先按父级排序,然后对每个分支进行第二次排序。这似乎效率低下
编辑:示例排序顺序错误。我已经改正了
编辑以澄清:每个嵌套分支都需要显示在父值的正下方,而不是分支的末尾
编辑:进一步更正数据。编辑:删除此项,它不起作用 这是我做的最好的了。哪个应该排序呢。我还没有测试过。我会留下最好的答案,看看是否有人能改进它
data.sort(function(a, b) {
return a.parent - b.parent;
});
var changes = true;
while (changes){
changes = false;
for (var i = 1; i < data.length; i++) {
if(data[i].parent === data[i-1].parent && data[i].sort < data[i-1].sort){
var tmp = data[i-1];
data[i-1] = data[i];
data[i] = tmp;
changes = true;
}
}
}
data.sort(函数(a,b){
返回a.parent-b.parent;
});
var变化=真;
while(更改){
更改=错误;
对于(变量i=1;i
经过多次尝试,我终于想出了这个办法。它可以工作,但不是很优雅。也可以将其抽象到自己的类中
// Initial sort places items in the correct sort order.
data.sort(function(a, b) {
return a.sort - b.sort;
});
vat top_parent_id = 1; // The value of an items parent if it is a top level item.
var sorted = []; // Empty array to copy sorted elements into.
var skipped = true; // flag to indicate if any elements have been skipped.
var skip_count = 0; // Counter to prevent infinite loops.
// Loop until no elements have been skipped.
//This loops through each level of the tree until all nested branches have been added
while(skipped){
skipped = false;
skip_count++;
if(skip_count === 50){ // Maximum tree depth.
throw "Error in sorted data or tree depth too great.";
break;
}
// Loop through the data in reverse order; this preserves the branch sort order.
for (var i = data.length - 1; i >= 0; i--) {
var item = data[i];
// Skip if this element has already been processed.
if(item.done)
continue;
// If this is this a top level item, then insert and continue.
if(item.parent == top_parent_id){
sorted.splice(0, 0, item); // Insert at the start; last item is the top sort.
item.done = true;
continue;
}
// Loop the new array to try and find this items parent.
var found = false;
for (var j = 0; j < sorted.length; j++) {
if(item.parent === sorted[j].index){
sorted.splice(j + 1, 0, item);
found = true;
item.done = true;
break;
}
}
// If a place for an item has not been found then skip it for now so it can be tested again on the next iteration.
if(!found){
skipped = true;
}
}
}
data = sorted;
//初始排序将项目按正确的排序顺序放置。
数据排序(函数(a,b){
返回a.sort-b.sort;
});
增值税顶部父项id=1;//项目父级的值(如果它是顶级项目)。
变量排序=[];//将已排序元素复制到的空数组。
var skipped=true;//指示是否跳过了任何元素的标志。
var skip_count=0;//计数器以防止无限循环。
//循环,直到没有跳过任何元素。
//这将循环遍历树的每个级别,直到添加了所有嵌套的分支
while(跳过){
跳过=错误;
跳过计数++;
如果(跳过计数===50){//最大树深度。
抛出“排序数据错误或树深度太大”;
打破
}
//以相反的顺序遍历数据;这将保留分支排序顺序。
对于(var i=data.length-1;i>=0;i--){
var项目=数据[i];
//如果此元素已被处理,则跳过。
如果(项目完成)
继续;
//如果这是顶级项目,请插入并继续。
if(item.parent==top\u parent\u id){
sorted.splice(0,0,item);//在开头插入;最后一项是最上面的排序。
item.done=true;
继续;
}
//循环新数组以尝试查找此项的父项。
var=false;
对于(var j=0;j
这不是您最初的方法,但您可以根据数据构建实际的树,如下所示:
function TreeNode(data) {
this.data = data;
this.parent = null;
this.children = [];
}
TreeNode.comparer = function (a, b) {
return a.data.sort < b.data.sort ? 0 : 1;
};
TreeNode.prototype.sortRecursive = function () {
this.children.sort(TreeNode.comparer);
for (var i=0, l=this.children.length; i<l; i++) {
this.children[i].sortRecursive();
}
return this;
};
function toTree(data) {
var nodeById = {}, i = 0, l = data.length, node;
nodeById[0] = new TreeNode(); // that's the root node
for (i=0; i<l; i++) { // make TreeNode objects for each item
nodeById[ data[i].index ] = new TreeNode(data[i]);
}
for (i=0; i<l; i++) { // link all TreeNode objects
node = nodeById[ data[i].index ];
node.parent = nodeById[node.data.parent];
node.parent.children.push(node);
}
return nodeById[0].sortRecursive();
}
tree.walk(function () { console.log(this.data) }, true);
这样称呼它:
function TreeNode(data) {
this.data = data;
this.parent = null;
this.children = [];
}
TreeNode.comparer = function (a, b) {
return a.data.sort < b.data.sort ? 0 : 1;
};
TreeNode.prototype.sortRecursive = function () {
this.children.sort(TreeNode.comparer);
for (var i=0, l=this.children.length; i<l; i++) {
this.children[i].sortRecursive();
}
return this;
};
function toTree(data) {
var nodeById = {}, i = 0, l = data.length, node;
nodeById[0] = new TreeNode(); // that's the root node
for (i=0; i<l; i++) { // make TreeNode objects for each item
nodeById[ data[i].index ] = new TreeNode(data[i]);
}
for (i=0; i<l; i++) { // link all TreeNode objects
node = nodeById[ data[i].index ];
node.parent = nodeById[node.data.parent];
node.parent.children.push(node);
}
return nodeById[0].sortRecursive();
}
tree.walk(function () { console.log(this.data) }, true);
这将产生:
{ index: 4, sort: 4, parent: 0}
{ index: 2, sort: 7, parent: 0}
{ index: 1, sort: 10, parent: 0}
{ index: 5, sort: 13, parent: 1}
{ index: 8, sort: 6, parent: 5}
{ index: 7, sort: 2, parent: 8}
{ index: 6, sort: 20, parent: 5}
{ index: 3, sort: 15, parent: 1}
{索引:4,排序:4,父级:0}
{索引:2,排序:7,父级:0}
{索引:1,排序:10,父级:0}
{索引:5,排序:13,父级:1}
{索引:8,排序:6,父级:5}
{索引:7,排序:2,父级:8}
{索引:6,排序:20,父级:5}
{索引:3,排序:15,父级:1}
使用更复杂的有效负载函数来实现其他效果,例如在带有jQuery的表中添加表行或将项添加到
框中。上面的Tomalak请求,我发布了他们答案的单例版本。这是:
/**
* Represents sorted results in a tree structure.
*/
Tree = (function() {
/**
*
* @type {Object} nodes Holds all the nodes in a flat format.
* @type {Object} nodes.data The data that is held in this node.
* @type {Object} nodes.parent Points to the parent object of this node.
* @type {Array} nodes.children An array of the child nodes of this node.
*/
var nodes = {};
/**
* @type {Object} root_node A Reference to the root node in nodes.
*/
var root_node;
/**
* A sort function to sort the nodes by the data.sort value in each node.
* @param {Number} a The first node to compare
* @param {Number} b The second node to compare
* @return {Boolean} Swap these nodes or not.
*/
var comparer = function (a, b) {
return a.data.sort < b.data.sort ? 0 : 1;
};
/**
* Sorts all the nodes so that they are in the correct order according to each nodes data.sort value.
* @param {Object} node A reference to the node in the nodes object.
*/
var sortRecursive = function (node) {
node.children.sort(comparer);
var len = node.children.length;
for (var i = 0 ; i < len ; i++) {
sortRecursive(node.children[i]);
}
};
/**
* Create a new node with the passed in data.
* @param {Object} data The data that is associated with this node.
*/
var create_node = function(data){
var node = {
data : data,
parent : null,
children : []
};
return node;
};
return {
/**
* Create a new tree of data
* @param {Array} data An array of data objects to transorm into a tree.
* @param {Array} data[].index The id of this node
* @param {Array} data[].parent The parent id of this node.
* @param {Number} root_id Id of the root node.
*/
create : function(data, root_id){
// Clear any previous data
nodes = {};
var i;
var len = data.length;
// Create an empty root node
nodes[root_id] = create_node({});
root_node = nodes[root_id];
// Make node objects for each data item
for (i=0; i<len; i++) {
if(typeof data[i].sort !== "undefined")
nodes[ data[i].index ] = create_node(data[i]);
}
// Link all TreeNode objects
for (i=0; i<len; i++) {
var node = nodes[data[i].index];
node.parent = nodes[node.data.parent];
node.parent.children.push(node);
}
sortRecursive(nodes[root_id]);
},
/**
* Walk through the nodes in nested and then sorted order, calling the passed in callback for each node.
* @param {Function} callback A callback function to call for each node.
* @param {Boolean} recursive Should the walkback be recursive, or just fetch the top level results.
* @param {Object|Undefined} node The node that is currently being walked.
* Ommit this value and the root node will be used.
*/
walk : function(callback, recursive, node) {
if(typeof node == "undefined")
node = root_node;
for (var i = 0, len = node.children.length; i < len; i++) {
var child = node.children[i];
callback.apply(child, Array.prototype.slice.call(arguments, 2));
if (recursive)
arguments.callee(callback, recursive, child);
}
}
};
})();
使用以下命令获取已排序的数组:
var sorted = [];
Tree.walk(function(){
sorted.push(this.data);
}, true);
这个算法给我的结果和我的完全一样。这与您在问题中要求的不同。谢谢Tomalak,这是一个很好的OOJavaScript。也比我的效率高。这也是递归的一个很好的例子。@SystemicPlumble:谢谢。还可以看到几分钟前添加的功能。再次感谢。我决定做基准测试。你的答案比我的快250倍。出于好奇,我随后将你的答案转换为单例闭包,并获得了进一步的10%。不知道为什么。它只能处理一棵树,因为它是一个单体,但对于我的用例来说,这很好。很好!如果你能把这些代码作为你自己的答案发布,我想看看。我已经在下面发布了。为了反映本文中的示例数据,我更改了一些变量名。希望不会破坏任何东西。+1用于分享您的解决方案。仅供参考。
arguments.callee
已被弃用。感谢您对arguments.callee的介绍
Tree.create(unsorted_data, parent_id);
var sorted = [];
Tree.walk(function(){
sorted.push(this.data);
}, true);