[英]Building a nested tree of data from plain array of objects
I have an array of objects, like those ones: 我有一系列对象,例如那些对象:
{
"short_id": "2p45q",
"path": "/",
"name": {
"en-US": "IndustrialDesign"
}
}
...
{
"short_id": "2q56r",
"path": "/2p45q/",
"name": {
"en-US": "Automotive"
}
}
I must iterate over each element of the array and check the path
, then find the parent of the element and push it in a new array property of that parent called sub
. 我必须遍历数组的每个元素并检查
path
,然后找到元素的父级并将其推入该父级的新数组属性sub
。 Each child can have a sub
property on it's own, thus being a parent of more children. 每个孩子可以自己拥有一个
sub
属性,因此可以成为更多孩子的父母。 The final result (for this example) would look like: 最终结果(对于此示例)如下所示:
{
"short_id": "2p45q",
"path": "/",
"name": {
"en-US": "Test A"
},
"sub": [
{
"short_id": "2q56r",
"path": "/2p45q/",
"name": {
"en-US": "Test A.1"
}
}
]
}
I have a working code (using this jsonpath lib ): 我有一个工作代码(使用此jsonpath lib ):
function(categories) {
var _categories = [];
angular.forEach(angular.copy(categories), function(_category) {
if (_category.path === "/") {
_categories.push(_category);
} else {
var _path = _category.path.split("/");
_path.pop();
var _parent = _path.pop();
jsonpath.apply(_categories, "$..[?(@.short_id=='" + _parent + "')]", function(obj) {
if(!obj.hasOwnProperty("sub")) {
obj.sub = [];
}
obj.sub.push(_category);
return obj;
});
}
});
return _categories;
}
but the performance is really bad, mainly because I'm querying the entire array for each iteration. 但是性能确实很差,主要是因为我要为每次迭代查询整个数组。
My question is how can I optimize my code? 我的问题是如何优化代码?
Notes: 笔记:
short_id
is exactly 5 characters long. short_id
均为5个字符。 short_id
can be [0-9a-z]
short_id
每个字符short_id
可以是[0-9a-z]
path
is guaranteed to start and end with a /
path
是保证开头和结尾/
Create another tmp object as Hashmap, so you can just use path and id to create a new key to store. 创建另一个tmp对象作为Hashmap,因此您可以仅使用path和id来创建一个新密钥来存储。
Logic : 逻辑:
If path is '/'
, its root, put to the _categories
array. 如果path为
'/'
, _categories
其根目录放入_categories
数组。
If not, check if the target parent is exist in the hashStore or not, if not, create a fake one, and put it self to target is sub
attr. 如果不存在,请检查hashStore中是否存在目标父对象,如果不存在,请创建一个假父对象,并将其自身作为目标
sub
属性。
For all element, create a key by _category.path + _category.short_id + '/'
, and check if its exist in the hashStore, if exist, the one in store should be a fake, get sub
from fake. 对于所有元素,请通过
_category.path + _category.short_id + '/'
创建一个密钥,并检查其是否在hashStore中存在,如果存在,则存储中的那个应该是假的,从假中获取sub
。 Then assign itself to the hashStore by created key. 然后通过创建的键将其自身分配给hashStore。
Use a key to decide whether the object exist in the map or not should be O(1). 使用键确定地图中是否存在对象应为O(1)。 So the performance of the this function should be O(n) while n is the number of element in origin list.
因此,此函数的性能应为O(n),而n是原始列表中元素的数量。
function buildTree(categories) {
var _categories = [];
var store = {};
angular.forEach(angular.copy(categories), function(_category) {
if (_category.path === '/') {
_categories.push(_category);
} else {
var target;
// If parent exist
if (typeof store[_category.path] !== 'undefined') {
// Check parent have sub or not, if not, create one.
target = store[_category.path];
if (typeof store[_category.path].sub === 'undefined') {
target.sub = [];
}
} else {
// Create fake parent.
target = {sub: []};
store[_category.path] = target;
}
// Push to parent's sub
target.sub.push(_category);
}
// Create key map
var key = _category.path + _category.short_id + '/';
// If fake object exist, get its sub;
if (typeof store[key] !== 'undefined') {
_category.sub = store[key].sub;
}
store[key] = _category;
});
return _categories;
}
This solution is more flexible in that it doesn't require knowledge of path length or correlation with short_id
该解决方案更加灵活,因为它不需要了解路径长度或与
short_id
相关的short_id
var source = [{ "short_id": "2p45q", "path": "/", "name": { "en-US": "IndustrialDesign" } }, { "short_id": "2q56r", "path": "/2p45q/", "name": { "en-US": "Automotive" } }]; function buildTree(arr) { var source = arr.slice(); source.sort(function(a, b) { return a.path.length <= b.path.length; }); var tree = source.splice(0, 1)[0]; tree.subo = {}; source.forEach(function(i) { var re = /[^\\/]*\\//g; var context = tree; while ((m = re.exec(i.path.substr(1))) !== null) { if (context.subo[m[0]] === undefined) { context.subo[m[0]] = i; i.subo = {}; return; } context = context.subo[m[0]]; } }); (function subOsub(i) { var keys = Object.keys(i.subo); if (keys.length > 0) { i.sub = []; for (var j = 0; j < keys.length; j++) { i.sub.push(i.subo[keys[j]]); subOsub(i.subo[keys[j]]); } } delete i.subo; })(tree); return tree; } alert(JSON.stringify(buildTree(source), null, ' '));
Well, just examine the path of each object to see where to put it. 好吧,只需检查每个对象的路径以查看将其放置在何处。 You just need a mapping of paths to objects.
您只需要映射到对象的路径即可。 Eg
例如
var objs = [ { "short_id": "2p45q", "path": "/", "name": { "en-US": "IndustrialDesign" } }, { "short_id": "blah", "path": "/2p45q/foo/", "name": { "blah": "blah" } }, { "short_id": "2q56r", "path": "/2p45q/", "name": { "en-US": "Automotive" } } ]; // map paths to objects (one iteration) var path_to_obj = {}; objs.forEach(function(obj){ path_to_obj[obj.path] = obj; }); // add objects to the sub array of their parent (one iteration) objs.forEach(function(obj){ var parentpath = obj.path.replace(/[^\\/]*\\/$/, ''); var parent = path_to_obj[parentpath]; if(parent){ parent.sub = parent.sub || []; parent.sub.push(obj); } }); var pre = document.createElement('pre'); pre.innerHTML = 'Result:\\n' + JSON.stringify(path_to_obj['/'], null, 4); document.body.appendChild(pre);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.