I have an array of objects, Where each object has an id and a ParentId property (so they can be arranged in trees). They are in no particular order
A working example of the below code is on jsFiddle.
Index the tree by id and traverse it upwards, from each node, and count until you hit the root. By indexing first, we approach O(n) time complexity (depending on tree density). ****Updated to satisfy the sorting requirement, and allow exclusion of root node***:
function levelAndSort(data, startingLevel) {
// indexes
var indexed = {}; // the original values
var nodeIndex = {}; // tree nodes
var i;
for (i = 0; i < data.length; i++) {
var id = data[i].id;
var node = {
id: id,
level: startingLevel,
children: [],
sorted: false
};
indexed[id] = data[i];
nodeIndex[id] = node;
}
// populate tree
for (i = 0; i < data.length; i++) {
var node = nodeIndex[data[i].id];
var pNode = node;
var j;
var nextId = indexed[pNode.id].parentId;
for (j = 0; nextId in nodeIndex; j++) {
pNode = nodeIndex[nextId];
if (j == 0) {
pNode.children.push(node.id);
}
node.level++;
nextId = indexed[pNode.id].parentId;
}
}
// extract nodes and sort-by-level
var nodes = [];
for (var key in nodeIndex) {
nodes.push(nodeIndex[key]);
}
nodes.sort(function(a, b) {
return a.level - b.level;
});
// refine the sort: group-by-siblings
var retval = [];
for (i = 0; i < nodes.length; i++) {
var node = nodes[i];
var parentId = indexed[node.id].parentId;
if (parentId in indexed) {
var pNode = nodeIndex[parentId];
var j;
for (j = 0; j < pNode.children.length; j++) {
var child = nodeIndex[pNode.children[j]];
if (!child.sorted) {
indexed[child.id].level = child.level;
retval.push(indexed[child.id]);
child.sorted = true;
}
}
}
else if (!node.sorted) {
indexed[node.id].level = node.level;
retval.push(indexed[node.id]);
node.sorted = true;
}
}
return retval;
}
// level 0 (root) excluded
var startingLevel = 1;
var someData = [
{id : "id-1", parentId : "id-0"},
{id : "id-2", parentId : "id-0"},
{id : "id-3", parentId : "id-2"},
{id : "id-4", parentId : "id-3"},
{id : "id-5", parentId : "id-4"},
{id : "id-6", parentId : "id-4"},
{id : "id-7", parentId : "id-0"},
{id : "id-8", parentId : "id-1"},
{id : "id-9", parentId : "id-7"},
{id : "id-10", parentId : "id-1"},
{id : "id-11", parentId : "id-1"},
{id : "id-12", parentId : "id-1"}
];
var outputArray = levelAndSort(someData, startingLevel);

If you change the input order, the sort comes out a little different, but it's still correct (i.e., in level-order, grouped by sibling).