Pregunta cómo codificar estos datos a la estructura padre / hijos en JSON


Estoy trabajando con d3.js para visualizar familias de animales (organismos) (hasta 4000 a la vez) como un gráfico de árbol, aunque la fuente de datos podría ser una lista de directorios o una lista de objetos con espacios de nombres. mis datos se ven así:

json = {
    organisms:[
        {name: 'Hemiptera.Miridae.Kanakamiris'},
        {name: 'Hemiptera.Miridae.Neophloeobia.incisa'},
        {name: 'Lepidoptera.Nymphalidae.Ephinephile.rawnsleyi'},
        ... etc ...
    ]
}

mi pregunta es: estoy tratando de encontrar la mejor manera de convertir los datos anteriores a la estructura jerárquica de datos padre / hijos tal como la utilizan varias visualizaciones d3 como treemap (para el ejemplo de datos ver flare.json en el directorio d3 / examples / data /). Aquí hay un ejemplo de la estructura de datos deseada:

{"name": "ROOT",
 "children": [
        {"name": "Hemiptera",
         "children": [
             {"name": "Miridae",
              "children": [
                  {"name": "Kanakamiris", "children":[]},
                  {"name": "Neophloeobia",
                   "children": [
                       {"name": "incisa", "children":[] }
                   ]}
              ]}
         ]},
        {"name": "Lepidoptera",
         "children": [
             {"name": "Nymphalidae",
              "children": [
                  {"name": "Ephinephile",
                   "children": [
                       {"name": "rawnsleyi", "children":[] }
                   ]}
              ]}
         ]}
    ]}
}

EDIT: encerró toda la estructura de datos original deseada dentro de una ROOT nodo, para cumplir con la estructura de los ejemplos d3, que tienen solo un nodo principal maestro.

Estoy buscando entender un patrón de diseño general, y como una bonificación me encantaría ver algunas soluciones en javascript, php, (o incluso python). javascript es mi preferencia. Con respecto a php: los datos que estoy usando provienen de una llamada a una base de datos por un script php que codifica los resultados como json. los resultados de la base de datos en la secuencia de comandos php es una matriz ordenada (ver a continuación) si es que sirve para respuestas basadas en php.

Array
(
    [0] => Array
        (
            ['Rank_Order'] => 'Hemiptera'
            ['Rank_Family'] => 'Miridae'
            ['Rank_Genus'] => 'Kanakamiris'
            ['Rank_Species'] => ''
        ) ........

dónde: 'Rank_Order' isParentOf 'Rank_Family' isParentOf 'Rank_Genus' isParentOf 'Rank_Species'

Hice una pregunta similar centrada en una solución de php aquí, pero la única respuesta no está funcionando en mi servidor, y no entiendo muy bien lo que está pasando, entonces quiero hacer esta pregunta desde una perspectiva de diseño de patrón, e incluir una referencia a mi uso real que está en javascript y d3. js.


14
2017-08-26 01:01


origen


Respuestas:


Lo siguiente es específico de la estructura que ha proporcionado, podría hacerse más genérico con bastante facilidad. Estoy seguro de que addChild la función se puede simplificar. Espero que los comentarios sean útiles.

function toHeirarchy(obj) {

  // Get the organisms array
  var orgName, orgNames = obj.organisms;

  // Make root object
  var root = {name:'ROOT', children:[]};

  // For each organism, get the name parts
  for (var i=0, iLen=orgNames.length; i<iLen; i++) {
    orgName = orgNames[i].name.split('.');

    // Start from root.children
    children = root.children;

    // For each part of name, get child if already have it
    // or add new object and child if not
    for (var j=0, jLen=orgName.length; j<jLen; j++) {
      children = addChild(children, orgName[j]);      
    }
  }
  return root;

  // Helper function, iterates over children looking for 
  // name. If found, returns its child array, otherwise adds a new
  // child object and child array and returns it.
  function addChild(children, name) {

    // Look for name in children
    for (var i=0, iLen=children.length; i<iLen; i++) {

      // If find name, return its child array
      if (children[i].name == name) {
        return children[i].children;        
      }
    }
    // If didn't find name, add a new object and 
    // return its child array
    children.push({'name': name, 'children':[]});
    return children[children.length - 1].children;
  }
}

7
2017-08-26 07:57



Dada tu entrada inicial, creo que algo como el siguiente código producirá tu salida deseada. No me imagino que esta sea la manera más bonita de hacerlo, pero es lo que vino a mi mente en ese momento.

Parecía más fácil preprocesar los datos para dividir primero la matriz inicial de cadenas en una matriz de matrices como esta:

[
   ["Hemiptera","Miridae","Kanakamiris" ],
   ["Hemiptera","Miridae","Neophloeobia","incisa" ],
   //etc
]

... y luego procesar eso para obtener un objeto que funcione de la siguiente forma:

  working = {
       Hemiptera : {
           Miridae : {
              Kanakamiris : {},
              Neophloeobia : {
                  incisa : {}
              }
           }
       },
       Lepidoptera : {
           Nymphalidae : {
              Ephinephile : {
                  rawnsleyi : {}
              }
           }
       }
    }

... porque trabajar con objetos en lugar de matrices hace que sea más fácil probar si ya existen elementos secundarios. Después de haber creado la estructura anterior, la proceso una vez más para obtener el resultado final deseado. Asi que:

// start by remapping the data to an array of arrays
var organisms = data.organisms.map(function(v) {
        return v.name.split(".");
    });

// this function recursively processes the above array of arrays
// to create an object whose properties are also objects
function addToHeirarchy(val, level, heirarchy) {
    if (val[level]) {
        if (!heirarchy.hasOwnProperty(val[level]))
            heirarchy[val[level]] = {};
        addToHeirarchy(val, level + 1, heirarchy[val[level]]);
    }
}
var working = {};    
for (var i = 0; i < organisms.length; i++)
    addToHeirarchy(organisms[i], 0, working);

// this function recursively processes the object created above
// to create the desired final structure
function remapHeirarchy(item) {
    var children = [];
    for (var k in item) {
        children.push({
            "name" : k,
            "children" : remapHeirarchy(item[k])
        });
    }
    return children;
}

var heirarchy = {
    "name" : "ROOT",
    "children" : remapHeirarchy(working)
};

Manifestación: http://jsfiddle.net/a669F/1/


5
2017-08-26 03:12



Una respuesta alternativa a mi propia pregunta ... En el último día he aprendido mucho más sobre d3.js y en relación con esta pregunta d3.nest () con .key () y .entries () es mi amigo (todas las funciones de d3). Esta respuesta implica cambiar los datos iniciales, por lo que puede no calificar como una buena respuesta a la pregunta específica que hice. Sin embargo, si alguien tiene una pregunta similar y puede cambiar las cosas en el servidor, esta es una solución bastante simple:

devolver los datos de la base de datos en este formato:

json = {'Organisms': [
    { 'Rank_Order': 'Hemiptera',
      'Rank_Family': 'Miridae',
      'Rank_Genus': 'Kanakamiris',
      'Rank_Species': '' },
    {}, ...
]}

Luego usando d3.nest ()

organismNest = d3.nest()
    .key(function(d){return d.Rank_Order;})
    .key(function(d){return d.Rank_Family;})
    .key(function(d){return d.Rank_Genus;})
    .key(function(d){return d.Rank_Species;})
    .entries(json.Organism);

esto vuelve:

{
key: "Hemiptera"
  values: [
    {
      key: "Cicadidae"
      values: [
        {
          key: "Pauropsalta "
          values: [
            {
              key: "siccanus"
              values: [
                       Rank_Family: "Cicadidae"
                       Rank_Genus: "Pauropsalta "
                       Rank_Order: "Hemiptera"
                       Rank_Species: "siccanus"
                       AnotherOriginalDataKey: "original data value"

etc etc, nested and lovely

Esto devuelve algo muy similar a la matriz que describí como mi formato deseado en la pregunta anterior, con algunas diferencias. En particular, no hay ningún elemento RAOT que lo rodee y también que las claves que originalmente quería eran "nombre" y "hijos" .nest () devuelve las claves como "clave" y "valores", respectivamente. Estas claves alternativas son lo suficientemente fáciles de usar en d3.js simplemente definiendo funciones de acceso a datos apropiadas (concepto básico de d3) ... pero eso está yendo más allá del alcance original de la pregunta ... esperanza que ayuda a alguien también


2
2017-09-01 03:37