Как закодировать эти данные в структуру parent / children в JSON
Я работаю с d3.js, чтобы визуализировать семейства животных (организмов) (до 4000 одновременно) в виде древовидного графа, хотя источником данных также может быть список каталогов или список объектов пространства имен. мои данные выглядят так:
json = {
organisms:[
{name: 'Hemiptera.Miridae.Kanakamiris'},
{name: 'Hemiptera.Miridae.Neophloeobia.incisa'},
{name: 'Lepidoptera.Nymphalidae.Ephinephile.rawnsleyi'},
... etc ...
]
}
мой вопрос: я пытаюсь найти лучший способ преобразовать вышеуказанные данные в иерархическую структуру данных parent / children, как это используется рядом визуализаций d3, таких как treemap (пример данных см. в flare.json в d3 / примеры / данные / каталог). Вот пример желаемой структуры данных:
{"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":[] }
]}
]}
]}
]}
}
РЕДАКТИРОВАТЬ: заключить всю оригинальную желаемую структуру данных в ROOT
узел, чтобы соответствовать структуре примеров d3, которые имеют только один главный родительский узел.
Я стремлюсь понять общий шаблон дизайна, и в качестве бонуса я хотел бы видеть некоторые решения в javascript, php (или даже в python). Я предпочитаю JavaScript Что касается php: данные, которые я на самом деле использую, поступают из обращения к базе данных через php-скрипт, который кодирует результаты как json. Результаты базы данных в сценарии php представляют собой упорядоченный массив (см. ниже), если он используется для ответов на основе 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.
3 ответа
Нижеследующее относится к предоставленной вами структуре, ее можно довольно легко сделать более общей. Я уверен, что функция 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;
}
}
Учитывая ваш начальный ввод, я верю, что что-то вроде следующего кода даст желаемый результат. Я не думаю, что это самый красивый способ сделать это, но это то, что пришло на ум в то время.
Казалось, проще всего предварительно обработать данные, чтобы сначала разбить исходный массив строк на массив массивов, например:
[
["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)
};
Альтернативный ответ на мой собственный вопрос.... За прошедший день я узнал немного больше о 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, а также в то время как ключи, которые я первоначально хотел, были "name" и "children" .nest() возвращает ключи как "key" и "values" соответственно. Эти альтернативные ключи достаточно просты в использовании в d3.js, просто определяя соответствующие функции доступа к данным (базовая концепция d3) ... но это выходит за рамки первоначального вопроса... надеюсь, что это кому-то тоже поможет