【问题标题】:JSON String to JS ArrayJSON字符串到JS数组
【发布时间】:2017-05-29 15:32:21
【问题描述】:

如何将文件夹结构 JSON String 转换为 JS Array。我关注 JSON 字符串

   [{ "Name": "A", "ParentName": "Config", "Type": "default" },
{ "Name": "SubA", "ParentName": "A", "Type": "default" },
{ "Name": "SubAFile", "ParentName": "SubA", "Type": "file" },
{ "Name": "B", "ParentName": "Config", "Type": "default" },
{ "Name": "C", "ParentName": "Config", "Type": "default" }]

我想用下面的格式制作 JS Array 对象

   var NewStr = [{
       "name": 'A',
       "id": 'A',
       "icon": 'fa fa-folder',
       "items": [{
           "title": "A",
           "icon": "fa fa-folder",
           "id": "A",
           "items": [{
               "name": "subA",
               "icon": "fa fa-folder",
               "id": "subA",
               "items": [{
                   "title": "SubA",
                   "icon": "fa fa-folder",
                   "id": "SubA",
                   "items": [{
                       "name": "SubAFile",
                       "icon": "fa fa-file"
                   }]
               }]
           }]
       }]
   }, {
       "name": 'B',
       "id": 'b',
       "icon": "fa fa-folder"
   }, {
       "name": 'C',
       "id": 'C',
       "icon": "fa fa-folder"
   }];

注意:我已包含 ParentName 以识别文件夹结构的层次结构。 ID 将与姓名相同。

对此有何建议?

谢谢..

【问题讨论】:

  • 第 1 步 - var array = JSON.parse(your string goes here) - 那么至少你有一个可以使用的 JS 数组 - 不过下一点更难
  • 是的,我被困在下一部分...
  • 您以重要的方式更改了所需的输出。你真的确定你需要这样吗?当您在其中添加与父级基本相同的节点并且仅在下一级添加子级时,将项目作为数组的目的是什么?这似乎是一个糟糕的设计结构。
  • @trincot 我正在尝试将来自multi-level-push-menu.make.rs 的导航菜单集成到我的项目对动态文件夹结构导航的需求中,这个插件以这种方式需要JS数组。

标签: javascript arrays json multidimensional-array nested


【解决方案1】:

首先使用JSON.parse 从有效的JSON 字符串生成对象。

JSON.parse() 方法解析 JSON 字符串,构造字符串描述的 JavaScript 值或对象。可以提供一个可选的 reviver 函数来在结果对象返回之前对其执行转换。

然后,您可以使用迭代方法生成树,并创建一个新对象,该对象具有引用插入或引用的父对象所需的属性,使用临时对象。

这也适用于未排序和嵌套的项目。

var data = [{ Name: "A", ParentName: "Config", Type: "default" }, { Name: "SubA", ParentName: "A", Type: "default" }, { Name: "SubAFile", ParentName: "SubA", Type: "file" }, { Name: "B", ParentName: "Config", Type: "default" }, { Name: "C", ParentName: "Config", Type: "default" }],
    tree = function (data, root) {
        var r = [], o = {};
        data.forEach(function (a) {
            var temp = { name: a.Name, icon: a.Type === 'file' ? 'fa fa-file' : 'fa fa-folder' };
            if (o[a.Name] && o[a.Name].items) {
                temp.items = o[a.Name].items;
            }
            o[a.Name] = temp;
            if (a.ParentName === root) {
                r.push(temp);
            } else {
                o[a.ParentName] = o[a.ParentName] || {};
                o[a.ParentName].items = o[a.ParentName].items || [];
                o[a.ParentName].items.push(temp);
            }
        });
        return r;
    }(data, 'Config');

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 非常感谢,这很有帮助,但是 config 下的根文件夹中可以有多个文件夹
  • 感谢您的编辑,它工作正常,但是我在集成您的解决方案时发现了更多编辑,如果您能提供帮助...抱歉没有仔细查看之前发布问题时所需的输入..跨度>
【解决方案2】:

您可以使用Map 按名称键入节点,并在使用reduce 迭代输入的同时构建树。如果父节点不存在,则为每个节点创建一个父节点。发生这种情况时,请记住这个新创建的父节点是树的根:它的子节点是您要生成的数组。

这是 ES6 代码:

// Sample input JSON parsed:
const items = JSON.parse('[{"Name":"A","ParentName":"Config","Type":"default"},{"Name":"new","ParentName":"A","Type":"file"},{"Name":"B","ParentName":"Config","Type":"default"},{"Name":"C","ParentName":"Config","Type":"default"}]');

const arr = items.reduce( ([nodes, root], {Name, ParentName, Type}) => {
    const node = Object.assign({ // create node
        name: Name,
        icon: Type == 'default' ? 'fa fa-folder' : 'fa fa-file'
    }, nodes.get(Name)); // add previously registered children, if any
    const parent = nodes.get(ParentName) || (root = {}); // create parent if not present
    parent.items = (parent.items || []).concat(node); // add current as child
    return [nodes.set(Name, node).set(ParentName, parent), root];
}, [new Map, {}] )[1].items; // start with empty map, return the items of the root

// Output result
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

更新问题后更新

在更新您的问题时,更改了所需的输出,具有更多嵌套级别:具有子节点的节点现在需要一个中间对象作为子对象(具有几乎相同的属性),而子节点又将子节点附加到它自己的 @987654326 @属性。

这是为此目的而改编的 ES6 代码:

function buildTree(folders) {
    const [nodes, root] = folders.reduce( ([nodes, root], {Name, ParentName, Type}) => {
        const node = Object.assign({ // create node
            name: Name,
            id: Name,
            icon: Type == 'default' ? 'fa fa-folder' : 'fa fa-file'
        }, nodes.get(Name)); // add previously registered children, if any
        const parent = nodes.get(ParentName) || (root = {}); // create parent if not present
        parent.items = (parent.items || []).concat(node); // add current as child
        return [nodes.set(Name, node).set(ParentName, parent), root];
    }, [new Map, {}] );
    // To add the extra intermediate levels (requested in updated question):
    nodes.forEach( node => {
        if (node.items) node.items = [{
            title: node.name,
            icon: node.icon,
            id: node.id,
            items: node.items
        }]
    });
    return root.items[0].items;
}    

// Sample JSON data, parsed
const folders = JSON.parse('[{ "Name": "A", "ParentName": "Config", "Type": "default" },{ "Name": "SubA", "ParentName": "A", "Type": "default" },{ "Name": "SubAFile", "ParentName": "SubA", "Type": "file" },{ "Name": "B", "ParentName": "Config", "Type": "default" },{ "Name": "C", "ParentName": "Config", "Type": "default" }]');

const arr = buildTree(folders);

// Output result
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

ES5 版本

对于几乎不支持 ES6 的浏览器(如 IE):

function buildTree(folders) {
    var result = folders.reduce(function (acc, obj) {
        var nodes = acc[0];
        var root = acc[1];
        var node = { // create node
            name: obj.Name,
            id: obj.Name,
            icon: obj.Type == 'default' ? 'fa fa-folder' : 'fa fa-file'
        };
        // add previously registered children, if any
        if (nodes[obj.Name]) node.items = nodes[obj.Name].items;
        var parent = nodes[obj.ParentName] || (root = {}); // create parent if not present
        parent.items = (parent.items || []).concat(node); // add current as child
        nodes[obj.Name] = node;
        nodes[obj.ParentName] = parent;
        return [nodes, root];
    }, [{}, {}] );
    // To add the extra intermediate levels (requested in updated question):
    for (var name in result[0]) {
        var node = result[0][name];
        if (node.items) node.items = [{
            title: node.name,
            icon: node.icon,
            id: node.id,
            items: node.items
        }];
    }
    return result[1].items[0].items;
}    

// Sample JSON data, parsed
var folders = JSON.parse('[{ "Name": "A", "ParentName": "Config", "Type": "default" },{ "Name": "SubA", "ParentName": "A", "Type": "default" },{ "Name": "SubAFile", "ParentName": "SubA", "Type": "file" },{ "Name": "B", "ParentName": "Config", "Type": "default" },{ "Name": "C", "ParentName": "Config", "Type": "default" }]');

var arr = buildTree(folders);

// Output result
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 感谢您的代码,它有帮助.. 但是我发现需要进行新的编辑,如果您能提供帮助?
  • 当然,但是您对问题的最后更新打破了逻辑。现在输入和输出如何关联?当人们花时间解决最初的问题时,以如此重要的方式改变问题是不公平的。此外,当它可以有重复时,它不再是真正的id
  • 对更改和没有仔细研究需求表示歉意。 Id 与 name 相同,并且不会相同,它只会在 title 元素中重复。我正在尝试将来自 multi-level-push-menu.make.rs 的导航菜单集成到我的文件夹结构导航代码中。
  • 我在答案中添加了一个新部分,它产生了新结构。
  • 谢谢Ton Sir..这很完美..感谢您的帮助:) :)
【解决方案3】:

这么棒的测验!我保证你会在几个小时内找到解决方案。你给出的是一种“反向二叉树”,而目前的 BigO 解决方案在我看来太丑陋了。

如果您不介意,我会发布 preSolution 草案,并继续思考更正确的方法来提高生产力并稍后对其进行编辑。

var tree = {}

function findParent(data, parentName){
    var parentExist = false;

    $.each(data, function(index, value){
        parentExist = (value.name == parentName);

        if(parentExist){
            moveChild(data, index, tree, tree[value.parentName]);
        } else {
            createParent(parentName, tree);
        }
    }
}

function moveChild(collectionIn, child, collectionOut, parent){
    collectionOut[parent].push(collectionIn[child]);
    splice(collectionIn[child], 1);
}

function createParent(parentName, targetTree);

$.each(data, function(index, val){
  findParent(index.parentName);
  
});

我要检查的提示:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    相关资源
    最近更新 更多