Подтвердить что ты не робот

Как кодировать эти данные в родительскую/дочернюю структуру в JSON

Я работаю с d3.js для визуализации семейств животных (организмов) (до 4000 за один раз) в качестве древовидного графика, хотя источником данных может быть также список каталогов или список объектов с именами. мои данные выглядят так:

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

мой вопрос: я пытаюсь найти лучший способ конвертировать приведенные выше данные в иерархическую структуру данных родительских/дочерних элементов, как это использует ряд визуализаций d3, таких как treemap (для примера данных см. flare.json в каталоге d3/examples/data/). Ниже приведен пример желаемой структуры данных:

{"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: заключена вся исходная структура требуемых данных внутри ROOT node, чтобы соответствовать структуре примеров d3, которые имеют только один основной родительский node.

Я хочу понять общий шаблон дизайна, и в качестве бонуса я бы хотел увидеть некоторые решения в javascript, php (или даже python). javascript - это мое предпочтение. Что касается php: данные, которые я фактически использую, поступают из вызова базы данных с помощью PHP скрипт, который кодирует результаты как json. результат базы данных в php script является упорядоченным массивом (см. ниже), если это полезно для ответов на основе php.

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

где: 'Rank_Order' isParentOf 'Rank_Family' isParentOf 'Rank_Genus' isParentOf 'Rank_Species'

Я задал аналогичный вопрос, сконцентрированный на php-решении здесь, но единственный ответ не работает на моем сервере, и я не совсем понимаю, что происходит, поэтому я хочу задать этот вопрос с точки зрения дизайна и включить ссылку на мое фактическое использование, которое находится в javascript и d3.js.

4b9b3361

Ответ 1

Ниже приведено описание структуры, которую вы предоставили, ее можно было бы сделать более общим довольно легко. Я уверен, что функция addChild может быть упрощена. Надеемся, что комментарии будут полезны.

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

Ответ 2

Учитывая ваш вводный ввод, я считаю, что что-то вроде следующего кода приведет к желаемому результату. Я не думаю, что это самый красивый способ сделать это, но это то, что приходило на ум в то время.

Казалось проще всего предварительно обработать данные, чтобы сначала разбить начальный массив строк на массив массивов, например:

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

... и затем обработать это, чтобы получить рабочий объект в форме примерно так:

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

... поскольку работа с объектами, а не с массивами, облегчает проверку того, существуют ли дочерние элементы. Создав вышеуказанную структуру, я затем обрабатываю ее в последний раз, чтобы получить окончательный желаемый результат. Итак:

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

Демо: http://jsfiddle.net/a669F/1/

Ответ 3

Альтернативный ответ на мой вопрос... В прошлый день я узнал гораздо больше о d3.js и по отношению к этому вопросу d3.nest() с .key() и .entries() - мой друг (все функции d3). Этот ответ включает в себя изменение исходных данных, поэтому он не может квалифицироваться как хороший ответ на конкретный вопрос, который я задал. Однако, если у кого-то есть аналогичный вопрос и он может изменить ситуацию на сервере, это довольно простое решение:

вернуть данные из базы данных в этом формате:

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

Затем с помощью 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);

это возвращает:

{
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

Это возвращает нечто очень похожее на массив, который я описал как мой желаемый формат выше в вопросе, с несколькими отличиями. В частности, нет всего закрывающего элемента ROOT, и в то время как для ключей, которые я изначально хотел, были "имя" и "дети".nest() возвращает ключи как "ключ" и "значения" соответственно. Эти альтернативные ключи достаточно легки для использования в d3.js, просто определяя соответствующие функции доступа к данным (базовая концепция d3)... но это выходит за рамки первоначальной области вопроса... надеюсь, что это тоже поможет кому-то.