简体   繁体   English

从对象的简单数组构建嵌套的数据树

[英]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: 笔记:

  • Each short_id is exactly 5 characters long. 每个short_id均为5个字符。
  • Each character in 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 : 逻辑:

  1. If path is '/' , its root, put to the _categories array. 如果path为'/'_categories其根目录放入_categories数组。

  2. 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属性。

  3. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM