2011-11-29 28 views
12

datos original JSON (tabla plana):Convertir matriz plana de objetos en matriz anidada de objetos

[ 
    {"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"}, 
    {"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"}, 
    {"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"}, 
    {"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"}, 
    {"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"}, 
    {"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"}, 
    {"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"} 
] 

Necesito llamar a la función como esta:

nest(data,["city","description","department"]) 

El primer parámetro es el entero conjunto de datos, el segundo es un conjunto de columnas que definen el nivel de anidamiento.

espera que la producción de JSON:

[ 
{key: "city", value: "Toronto", count: 1, children: 
    [ 
     {key: "description", value: "Programmer", count: 1, children: 
      [ 
       {key: "department", value: "Finance", count: 1} 
      ] 
     } 
    ] 
}, 
{key: "city", value: "Vancouver", count: 2, children: 
    [ 
     {key: "description", value: "Tester", count: 3, children: 
      [ 
       {key: "department", value: "Finance", count: 1}, 
       {key: "department", value: "QA", count: 1}, 
       {key: "department", value: "IT", count: 1} 
      ] 
     }, 
     {key: "description", value: "Manager", count: 1} 
    ] 
}, 

{key: "city", value: "New York", count: 2, children: 
    [ 
     {key: "description", value: "Tester", count: 1, children: 
      [ 
       {key: "department", value: "QA", count: 1} 
      ] 
     }, 
     {key: "description", value: "Manager", count: 1, children: 
      [ 
       {key: "department", value: "HR", count: 1} 
      ] 
     } 
    ] 
} 

]

He intentado escribir unas pocas funciones recursivas, pero sigo quedarse atascado cuando tengo que buscar dinámicamente el árbol para evitar la duplicación.

+1

Por qué '{clave: "departamento", valor:" Financiar", count: 1} 'en lugar de' {key: "department", value: "Finance", count: 0} '? Ese nodo no tiene hijos. –

+0

Bienvenido a [SO]; por favor lea el [faq]. – zzzzBov

+0

¿Qué es el conteo? Es 1 incluso cuando los niños son cero? – goat

Respuesta

9

Pensé que esta era una pequeña pregunta divertida, así que lo hice ... pero estoy de acuerdo con las personas que preguntaron "¿qué has intentado hasta ahora?". Normalmente, debe hablar sobre un problema específico.

// Groups a flat array into a tree. 
// "data" is the flat array. 
// "keys" is an array of properties to group on. 
function groupBy(data, keys) { 

    if (keys.length == 0) return data; 

    // The current key to perform the grouping on: 
    var key = keys[0]; 

    // Loop through the data and construct buckets for 
    // all of the unique keys: 
    var groups = {}; 
    for (var i = 0; i < data.length; i++) 
    { 
     var row = data[i]; 
     var groupValue = row[key]; 

     if (groups[groupValue] == undefined) 
     { 
      groups[groupValue] = new Array(); 
     } 

     groups[groupValue].push(row); 
    } 

    // Remove the first element from the groups array: 
    keys.reverse(); 
    keys.pop() 
    keys.reverse(); 

    // If there are no more keys left, we're done: 
    if (keys.length == 0) return groups; 

    // Otherwise, handle further groupings: 
    for (var group in groups) 
    { 
     groups[group] = groupBy(groups[group], keys.slice()); 
    } 

    return groups; 
} 

Llame al método como este:

var groupedData = groupBy(data, ["city","description","department"]); 

La salida de este método para sus datos es el siguiente:

{ 
    "Toronto": { 
     "Programmer": { 
      "Finance": [ 
       { 
        "id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1" 
       } 
      ] 
     } 
    }, 
    "Vancouver": { 
     "Tester": { 
      "Finance": [ 
       { 
        "id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1" 
       } 
      ], 
      "QA": [ 
       { 
        "id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1" 
       } 
      ], 
      "IT": [ 
       { 
        "id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1" 
       } 
      ] 
     }, 
     "Manager": { 
      "HR": [ 
       { 
        "id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1" 
       } 
      ] 
     } 
    }, 
    "New York": { 
     "Tester": { 
      "QA": [ 
       { 
        "id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1" 
       } 
      ] 
     }, 
     "Manager": { 
      "HR": [ 
       { 
        "id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1" 
       } 
      ] 
     } 
    } 
} 

Debido a que los grupos son todos los objetos JavaScript, usted don' Necesito ese miembro "contar". Simplemente puede usar la propiedad .length de la (s) matriz (es).

Recorra los grupos usando la sintaxis de javascript for (var group in groups).

+1

Eso fue realmente útil, gracias. – WeaponX86

+0

Una buena solución, guardando para más adelante. –

7

Es posible echar un vistazo al operador nest() de D3.js: https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js Esto es parte de D3, una biblioteca más grande, pero mirando rápidamente en el código Acabo de vincular a, yo no creo que esto tenga ningún dependencias, por lo que debería poder levantar el código aquí para usarlo en su propio proyecto. El uso es described here in the docs - encadena .key() métodos para definir las claves para cada capa de la estructura anidada. En su caso, este podría ser:

data = d3.nest() 
    .key(function(d) { return d.city }) 
    .key(function(d) { return d.description }) 
    .entries(data); 

La estructura de este escupe es un poco diferente de lo que tiene, pero es funcionalmente muy similares:

[ 
    { 
    "key": "Toronto", 
    "values": [ 
     { 
     "key": "Programmer", 
     "values": [ 
      { 
      "active": "1", 
      "city": "Toronto", 
      "department": "Finance", 
      "description": "Programmer", 
      "end_date": "2006-07-25", 
      "first_name": "Jason", 
      "id": "1", 
      "last_name": "Martin", 
      "salary": "1234.56", 
      "start_date": "1996-07-25" 
      }, 
      // etc ... 
     ] 
     } 
    ] 
    }, 
    // etc ... 
] 
+0

+1 Bueno uno ... tiene un par de beneficios sobre la versión rápida y sucia que armé, incluso; claves seleccionables por el desarrollador (lo que significa que sería fácil hacer que las teclas no distingan entre mayúsculas y minúsculas o calculadas) y clasificación. Personalmente, probablemente modificaría mi versión para agregar esas características en lugar de meterme en (otra) biblioteca ... ¡pero lindo descubrimiento! – Steve

+1

Gracias - no necesita toda la biblioteca, solo la función en el código al que he vinculado, que no tiene ninguna dependencia D3. Pero es cierto que podría ser excesivo para algunos proyectos. – nrabinowitz

+0

Definitivamente voy a echar un vistazo a esto, buen descubrimiento! – WeaponX86

0

Basándose en el ejemplo proporcionado por @ nrabinowitz, aquí está la función de la jerarquía con la API propuesta originalmente de pasar la colección y una serie de nombres de propiedades como argumentos, utilizando d3.nest bajo el capó:

function nest(data, keys) { 
    var nest = d3.nest(); 
    keys.forEach(function(k) { 
    nest.key(function(d) { 
     return d[k]; 
    }) 
    }); 
    return nest.entries(data); 
}