Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 是否基于字符串属性将对象数组转换为对象嵌套数组?_Javascript_Arrays_Tree_Javascript Objects_Lodash - Fatal编程技术网

Javascript 是否基于字符串属性将对象数组转换为对象嵌套数组?

Javascript 是否基于字符串属性将对象数组转换为对象嵌套数组?,javascript,arrays,tree,javascript-objects,lodash,Javascript,Arrays,Tree,Javascript Objects,Lodash,我遇到了一个问题,试图将对象的平面数组转换为基于name属性的对象的嵌套数组 将输入数组转换为类似所需输出数组结构的最佳方法是什么 var input = [ { name: 'foo', url: '/somewhere1', templateUrl: 'foo.tpl.html', title: 'title A', subtitle: 'description A' }, {

我遇到了一个问题,试图将对象的平面数组转换为基于name属性的对象的嵌套数组

输入
数组转换为类似
所需输出
数组结构的最佳方法是什么

var input = [
    { 
        name: 'foo', 
        url: '/somewhere1',
        templateUrl: 'foo.tpl.html',
        title: 'title A', 
        subtitle: 'description A' 
    },
    { 
        name: 'foo.bar', 
        url: '/somewhere2', 
        templateUrl: 'anotherpage.tpl.html', 
        title: 'title B', 
        subtitle: 'description B' 
    },
    { 
        name: 'buzz.fizz',
        url: '/another/place',
        templateUrl: 'hello.tpl.html',  
        title: 'title C',  
        subtitle: 'description C' 
    },
    { 
        name: 'foo.hello.world', 
        url: '/',
        templateUrl: 'world.tpl.html',
        title: 'title D',   
        subtitle: 'description D' 
    }
]

var desiredOutput = [
    {
        name: 'foo',
        url: '/somewhere1',
        templateUrl: 'foo.tpl.html',
        data: {
            title: 'title A',
            subtitle: 'description A'
        },
        children: [
            {
                name: 'bar',
                url: '/somewhere2', 
                templateUrl: 'anotherpage.tpl.html',
                data: {
                    title: 'title B', 
                    subtitle: 'description B'
                }
            },
            {
                name: 'hello',
                data: {},
                children: [
                    {
                        name: 'world',
                        url: '/',
                        templateUrl: 'world.tpl.html',
                        data: {
                            title: 'title D',   
                            subtitle: 'description D'
                        }
                    }
                ]
            }
        ]
    },
    {
        name: 'buzz',
        data: {},
        children: [
            {
                name: 'fizz',
                url: '/',
                templateUrl: 'world.tpl.html',
                data: {
                    title: 'title C',   
                    subtitle: 'description C'
                }
            }
        ]
    }
]
注意:输入数组中对象的顺序不能保证。 这段代码将在Node.js环境中运行,我愿意使用lodash之类的库来实现所需的输出


非常感谢您的帮助。

此解决方案仅使用本机JS方法。它当然可以优化,但我保持原样是为了更容易遵循(或者我希望是这样)。当JS通过引用传递对象时,我也注意不修改原始输入

var输入=[{
名称:“foo”,
url:“/somewhere1”,
templateUrl:'foo.tpl.html',
标题:“标题A”,
副标题:"描述A"
}, {
名称:“foo.bar”,
url:“/somewhere2”,
templateUrl:'anotherpage.tpl.html',
标题:“标题B”,
副标题:“描述B”
}, {
名称:“嗡嗡作响,嘶嘶作响”,
url:“/other/place”,
templateUrl:'hello.tpl.html',
标题:“标题C”,
副标题:"描述C"
}, {
名字:“富,你好,世界”,
url:“/”,
templateUrl:'world.tpl.html',
标题:“标题D”,
副标题:“描述D”
}];
//迭代输入数组元素
var desiredOutput=input.reduce(函数createOutput(arr,obj){
var name=obj.name.split('.');
//将输入元素对象复制为不修改原始输入
var newObj=Object.keys(obj).filter(函数skipName(key){
返回键!=='name';
}).reduce(函数copyObject(tempObj,键){
if(key.match(/url$/i)){
tempObj[key]=obj[key];
}
否则{
临时对象数据[键]=对象[键];
}
返回tempObj;
},{name:names[names.length-1],数据:{});
//使用可能的递归构建新的输出数组
buildArray(arr、names、newObj);
返回arr;
}, []);
document.write(“”+JSON.stringify(desiredOutput,null,4)+“”);
//按名称属性搜索数组元素对象的帮助器函数
函数findIndexByName(arr,name){
对于(变量i=0,len=arr.length;i
使用Lodash(因为你到底为什么要在没有实用程序库的情况下操作复杂数据)。给你

它的工作原理是重塑初始输入,以便将名称拆分为数组并添加数据/子属性。然后,它通过
buildTree
减少数据,该树使用一个变异函数(:())在reduce中的给定路径插入当前项

奇怪的
if(!match)
部分确保在初始数据集中未使用URL等明确指定缺少的段时,将其添加进来

最后两行实际上完成了这项工作,可能是在一个小函数中,它可以与一些JSDoc一起完成。遗憾的是,我没有完全递归,我依靠数组变异将route对象插入树的深处


不过,应该足够简单。以下是我基于Lodash的尝试

首先,我发现
\.set
可以理解深度嵌套的对象表示法,因此我使用它来构建编码父子关系的树:

{
    "foo": {
        "name": "foo",
        "url": "/somewhere1",
        "templateUrl": "foo.tpl.html",
        "title": "title A",
        "subtitle": "description A",
        "bar": {
            "name": "foo.bar",
            "url": "/somewhere2",
            "templateUrl": "anotherpage.tpl.html",
            "title": "title B",
            "subtitle": "description B"
        },
        "hello": {
            "world": {
                "name": "foo.hello.world",
                "url": "/",
                "templateUrl": "world.tpl.html",
                "title": "title D",
                "subtitle": "description D"
            }
        }
    },
    "buzz": {
        "fizz": {
            "name": "buzz.fizz",
            "url": "/another/place",
            "templateUrl": "hello.tpl.html",
            "title": "title C",
            "subtitle": "description C"
        }
    }
}
这将产生:

var buildChildrenRecursively = function(tree) {
  var children = _.keys(tree).filter(k => _.isObject(tree[k]));
  if (children.length > 0) {

    // Step 1 of reformatting: move children to children
    var newtree = _.omit(tree, children);
    newtree.children = children.map(k => buildChildrenRecursively(tree[k]));

    // Step 2 of reformatting: deal with long chains with missing intermediates
    children.forEach((k, i) => {
      if (_.keys(newtree.children[i]).length === 1) {
        newtree.children[i].data = {};
        newtree.children[i].name = k;
      }
    });

    // Step 3 of reformatting: move title/subtitle to data; keep last field in name
    newtree.children = newtree.children.map(function(obj) {
      if ('data' in obj) {
        return obj;
      }
      var newobj = _.omit(obj, 'title,subtitle'.split(','));
      newobj.data = _.pick(obj, 'title,subtitle'.split(','));
      newobj.name = _.last(obj.name.split('.'));
      return newobj;
    });

    return (newtree);
  }
  return tree;
};

var result = buildChildrenRecursively(tree).children;
事实上,这与期望的输出相差甚远,但孩子们的名字与其他属性(如
title
)一起显示为属性

然后是一个费劲的过程,编写了一个递归函数,它采用了这个中间树,并以您希望的方式重新格式化它:

  • 它首先需要找到子属性,并将它们移动到
    子属性数组中
  • 然后,它必须处理这样一个事实:对于长链,像
    foo.hello.world
    中的
    hello
    这样的中间节点没有任何数据,因此它必须插入
    data:{}
    name
    属性
  • 最后,它清理了剩下的内容:将标题和副标题放在
    数据
    属性中,并清理所有仍然完全限定的
    名称
  • 守则:

    [
        {
            "name": "foo",
            "url": "/somewhere1",
            "templateUrl": "foo.tpl.html",
            "children": [
                {
                    "name": "bar",
                    "url": "/somewhere2",
                    "templateUrl": "anotherpage.tpl.html",
                    "data": {
                        "title": "title B",
                        "subtitle": "description B"
                    }
                },
                {
                    "children": [
                        {
                            "name": "world",
                            "url": "/",
                            "templateUrl": "world.tpl.html",
                            "data": {
                                "title": "title D",
                                "subtitle": "description D"
                            }
                        }
                    ],
                    "data": {},
                    "name": "hello"
                }
            ],
            "data": {
                "title": "title A",
                "subtitle": "description A"
            }
        },
        {
            "children": [
                {
                    "name": "fizz",
                    "url": "/another/place",
                    "templateUrl": "hello.tpl.html",
                    "data": {
                        "title": "title C",
                        "subtitle": "description C"
                    }
                }
            ],
            "data": {},
            "name": "buzz"
        }
    ]
    
    输出:

    var input = [
        {
            name: 'foo',
            url: '/somewhere1',
            templateUrl: 'foo.tpl.html',
            title: 'title A',
            subtitle: 'description A'
        },
        {
            name: 'foo.bar',
            url: '/somewhere2',
            templateUrl: 'anotherpage.tpl.html',
            title: 'title B',
            subtitle: 'description B'
        },
        {
            name: 'buzz.fizz',
            url: '/another/place',
            templateUrl: 'hello.tpl.html',
            title: 'title C',
            subtitle: 'description C'
        },
        {
            name: 'foo.hello.world',
            url: '/',
            templateUrl: 'world.tpl.html',
            title: 'title D',
            subtitle: 'description D'
        }
    ];
    
    var nameList = _.sortBy(_.pluck(input, 'name'));
    var structure = {};
    
    var mapNav = function(name, navItem) {
        return {
            name : name,
            url : navItem.url,
            templateUrl : navItem.templateUrl,
            data : { title : navItem.title, subtitle : navItem.subtitle },
            children : []
        };
    };
    
    _.map(nameList, function(fullPath) {
        var path = fullPath.split('.');
        var parentItem = {};
        _.forEach(path, function(subName, index) {
            var navItem = _.find(input, { name : fullPath });
            var item = mapNav(subName, navItem);
            if (index == 0) {
                structure[subName] = item;
            } else {
                parentItem.children.push(item);
            }
            parentItem = item;
        });
    });
    
    
    var finalStructure = Object.keys(structure).map(function(key) {
        return structure[key];
    });
    
    console.log(finalStructure);  
    

    这个解决方案不使用递归,它使用指向对象图中前一项的引用指针

    注意这个解决方案在这里使用了lodash.JSFiddle示例


    这是一个使用lodash的完全无递归的方法。当我想到
    .set
    .get
    有多好时,我想到了这一点,我意识到我可以用
    子对象的序列替换对象“路径”

    首先,构建一个对象/哈希表,其键等于
    input
    数组的
    name
    属性:

    var partial = _.flatten(
        input.map(o =>
                  {
                    var newobj = _.omit(o, 'title,subtitle'.split(','));
                    newobj.data = _.pick(o, 'title,subtitle'.split(','));
                    return newobj;
                  })
            .map(o => {
              var parents = o.name.split('.').slice(0, -1);
              var missing =
                  parents.map((val, idx) => parents.slice(0, idx + 1).join('.'))
                      .filter(name => !(name in names))
                      .map(name => {
                        return {
                          name,
                          data : {},
                        }
                      });
    
              return missing.concat(o);
            }));
    partial = _.sortBy(partial, o => o.name.split('.').length);
    
    (不要试图
    JSON.stringify
    这个对象!因为它的值都是未定义的,所以它的计算结果是
    {}
    。)

    接下来,对每个元素应用两种转换:(1)将标题和副标题清理到子属性
    数据中,以及(2)这有点棘手,找到所有中间路径,如
    buzz
    foo.hello
    ,它们在
    input
    中没有表示,但它们的子级是。展平此数组,并按
    name
    字段中的
    数量对它们进行排序

    [
        {
            "name": "foo",
            "url": "/somewhere1",
            "templateUrl": "foo.tpl.html",
            "data": {
                "title": "title A",
                "subtitle": "description A"
            }
        },
        {
            "name": "buzz",
            "data": {}
        },
        {
            "name": "foo.bar",
            "url": "/somewhere2",
            "templateUrl": "anotherpage.tpl.html",
            "data": {
                "title": "title B",
                "subtitle": "description B"
            }
        },
        {
            "name": "buzz.fizz",
            "url": "/another/place",
            "templateUrl": "hello.tpl.html",
            "data": {
                "title": "title C",
                "subtitle": "description C"
            }
        },
        {
            "name": "foo.hello",
            "data": {}
        },
        {
            "name": "foo.hello.world",
            "url": "/",
            "templateUrl": "world.tpl.html",
            "data": {
                "title": "title D",
                "subtitle": "description D"
            }
        }
    ]
    
    这段代码可能看起来很吓人,但看到它的输出应该会有帮助
    var names = _.object(_.pluck(input, 'name'));
    // { foo: undefined, foo.bar: undefined, buzz.fizz: undefined, foo.hello.world: undefined }
    
    var partial = _.flatten(
        input.map(o =>
                  {
                    var newobj = _.omit(o, 'title,subtitle'.split(','));
                    newobj.data = _.pick(o, 'title,subtitle'.split(','));
                    return newobj;
                  })
            .map(o => {
              var parents = o.name.split('.').slice(0, -1);
              var missing =
                  parents.map((val, idx) => parents.slice(0, idx + 1).join('.'))
                      .filter(name => !(name in names))
                      .map(name => {
                        return {
                          name,
                          data : {},
                        }
                      });
    
              return missing.concat(o);
            }));
    partial = _.sortBy(partial, o => o.name.split('.').length);
    
    [
        {
            "name": "foo",
            "url": "/somewhere1",
            "templateUrl": "foo.tpl.html",
            "data": {
                "title": "title A",
                "subtitle": "description A"
            }
        },
        {
            "name": "buzz",
            "data": {}
        },
        {
            "name": "foo.bar",
            "url": "/somewhere2",
            "templateUrl": "anotherpage.tpl.html",
            "data": {
                "title": "title B",
                "subtitle": "description B"
            }
        },
        {
            "name": "buzz.fizz",
            "url": "/another/place",
            "templateUrl": "hello.tpl.html",
            "data": {
                "title": "title C",
                "subtitle": "description C"
            }
        },
        {
            "name": "foo.hello",
            "data": {}
        },
        {
            "name": "foo.hello.world",
            "url": "/",
            "templateUrl": "world.tpl.html",
            "data": {
                "title": "title D",
                "subtitle": "description D"
            }
        }
    ]
    
    var name2path = {'empty' : ''};
    var out = {};
    partial.forEach(obj => {
      var split = obj.name.split('.');
      var par = name2path[split.slice(0, -1).join('.') || "empty"];
      var path = par + 'children.' + (_.get(out, par + 'children') || []).length;
      name2path[obj.name] = path + '.';
      _.set(out, path, obj);
    });
    out = out.children;
    
    {
        "empty": "",
        "foo": "children.0.",
        "buzz": "children.1.",
        "foo.bar": "children.0.children.0.",
        "buzz.fizz": "children.1.children.0.",
        "foo.hello": "children.0.children.1.",
        "foo.hello.world": "children.0.children.1.children.0."
    }
    
    [
        {
            "name": "foo",
            "url": "/somewhere1",
            "templateUrl": "foo.tpl.html",
            "data": {
                "title": "title A",
                "subtitle": "description A"
            },
            "children": [
                {
                    "name": "foo.bar",
                    "url": "/somewhere2",
                    "templateUrl": "anotherpage.tpl.html",
                    "data": {
                        "title": "title B",
                        "subtitle": "description B"
                    }
                },
                {
                    "name": "foo.hello",
                    "data": {},
                    "children": [
                        {
                            "name": "foo.hello.world",
                            "url": "/",
                            "templateUrl": "world.tpl.html",
                            "data": {
                                "title": "title D",
                                "subtitle": "description D"
                            }
                        }
                    ]
                }
            ]
        },
        {
            "name": "buzz",
            "data": {},
            "children": [
                {
                    "name": "buzz.fizz",
                    "url": "/another/place",
                    "templateUrl": "hello.tpl.html",
                    "data": {
                        "title": "title C",
                        "subtitle": "description C"
                    }
                }
            ]
        }
    ]