Cómo convertir la estructura de carpetas JSON String a JS Array. He seguido la cadena 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" }]

Quiero hacer que el objeto JS Array salga de esto en el siguiente formato

   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"
   }];

Nota: ParentName que he incluido para identificar la jerarquía de la estructura de carpetas. La identificación será la misma que la del nombre.

¿Alguna sugerencia para esto?

Gracias..

1
user7417866 14 ene. 2017 a las 12:34

3 respuestas

La mejor respuesta

Puede usar un Map para marcar los nodos por nombre y construir el árbol mientras itera sobre la entrada con reduce. Para cada nodo, cree un nodo principal si aún no existe. Cuando esto suceda, recuerde a este padre recién creado como la raíz del árbol: sus hijos son la matriz que desea producir.

Aquí está el código 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; }

Actualizar después de una pregunta actualizada

En la actualización de su pregunta, se cambió el resultado deseado, con más niveles anidados: los nodos con hijos ahora necesitan un objeto intermedio como hijo (con casi las mismas propiedades) que a su vez tiene los nodos hijos unidos a su propio items propiedad.

Aquí está el código ES6 adaptado para ese propósito:

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; }

Versión ES5

Para los navegadores que tienen poca compatibilidad con ES6 (como 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; }
0
trincot 17 ene. 2017 a las 09:03

Primer uso JSON.parse para generar un objeto a partir de una cadena JSON válida.

El método JSON.parse() analiza una cadena JSON, construyendo el valor de JavaScript u objeto descrito por la cadena. Se puede proporcionar una función de reviver opcional para realizar una transformación en el objeto resultante antes de que se devuelva.

Luego, podría usar un enfoque iterativo para generar un árbol con la creación de un nuevo objeto con las propiedades deseadas para hacer referencia a objetos primarios insertados o referenciados, se utiliza un objeto temporal.

Esto también funciona para elementos sin clasificar y anidados.

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; }
1
Nina Scholz 14 ene. 2017 a las 17:29

¡Qué gran prueba! Te prometo que encontrarás la solución en un par de horas. Lo que diste es una especie de "árbol binario inverso" y la solución actual en términos de BigO se ve muy fea por mi cuenta.

Si no le importa, publicaré un borrador de preSolution y seguiré pensando en una forma más correcta de aumentar la productividad y editarlo un poco más tarde.

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);
  
});

Consejos que debo verificar:

0
Community 23 may. 2017 a las 12:08