2011-09-13 1 views
1

У меня есть яваскрипт массив объектов с объектами, которые выглядят так:Построение графа объекта из плоского массива объектов в JavaScript

Itemid
имя
parentItemId < == элементы верхнего уровня, не имеющие родителей, имеют null значение

Я хочу построить график, в котором родительские элементы содержат массивы детей, а у этих детей есть массивы детей, если это применимо.

Что такое хороший способ сделать это?

+0

вы можете попробовать плагин, как http://www.jqplot.com/ –

+0

Привет, Иосиф, интересный проект! но я не имел в виду диаграмму/график .. просто вложенный объект «graph» .. –

ответ

3
function objectGraph(items) 
{ 
    var items_by_id = {}; 
    var roots = []; 
    var i; 

    // Build an id->object mapping, so we don't have to go hunting for parents 
    for (i = 0; i < items.length; ++i) { 
     items_by_id[items[i].itemId] = items[i]; 
     items[i].children = []; 
    } 

    for (i = 0; i < items.length; ++i) { 
     var parentId = items[i].parentItemId; 
     // If parentId is null, this is a root; otherwise, it's parentId's kid 
     var nodes = (parentId === null) ? roots : items_by_id[parentId].children; 
     nodes.push(items[i]); 
    } 
    return roots; 
} 

Примечание Этот код дает каждому узлу children свойство, что пусто, если узел не имеет детей. Я лично считаю его более простым и последовательным, чем каждый узел, возможно, или, может быть, не имея детей; вы можете перебрать children, не беспокоясь о том, существует ли он. У листового узла будет children.length == 0.

Если вы можете гарантировать, что у вас есть ровно один корень, вы можете return roots[0]; вместо возврата массива.

+0

, поэтому вместо того, чтобы делать if (children), вы делаете if (children.length == 0) ... hmm Я предполагаю, что это имеет смысл, если вы хотите покрыть итерацию элементов, а затем решить, есть ли лист или нет. Обычно я делаю это наоборот. – James

+0

Вместо того, чтобы делать 'if (children)', вы должны делать 'if (children.length)'. В любом случае, вы можете работать нормально (проверяя существование этого списка, независимо от того, содержит ли он что-либо), но последнее означает, что это менее специализированный корпус, и вам не нужно беспокоиться о том, получите ли вы ошибки неопределенного объекта. Если вы хотите, вы можете лечить листовой узел, как любой другой, и перебирать своих детей; у вас просто не будет детей, чтобы что-либо сделать. :) Обратите внимание, что DOM работает аналогичным образом; каждый узел имеет список дочерних элементов, даже если этот список пуст. – cHao

+0

Достаточно, а также firefox (возможно, все браузеры) возвращают 0 для детей.длина пустого элемента DOM. Также мое решение делает то же самое LOL. – James

0

Это немного грубо, но это должно сработать, если я правильно собираю ваш вопрос. Он должен возвращать массив объектов верхнего уровня, у которых их дети правильно организованы под ними.

В качестве примечания, это будет работать для объектов уровня N, а не только на одном уровне.

var finalArray = []; 
var YOUR_RAW_ARRAY = []; 

var buildObjectGraph = function(inputArray){ 
    var i = 0, len = inputArray.length; 
    var returnVal = []; 

    for(;i<len;i++){ 
     if(inputArray[i].parentItemId === null){ 
      findChildren(inputArray[i], inputArray); 
      returnVal.push(inputArray[i]); 
     } 
    } 

    var findChildren = function(root){ 
     var i = 0, i2 = 0, len = rawDataArray.length, len2 = 0; 

     for(;i<len;i++){ 
      if(inputArray[i].parentItemId === root.itemId){ 
       if(root.children){ 
        root.children.push(inputArray[i]); 
       }else{ 
        root.children = []; 
        root.children.push(inputArray[i]); 
       } 
      } 
     } 

     //now call it recursively 
     len2 = root.children.length; 
     if(len2 > 0){ 
      for(;i2 < len2; i2++){ 
       findChildren(root.children[i2]); 
      } 
     } 
    }; 
    return returnVal; 
}; 

//Then execute it 
finalArray = buildObjectGraph(YOUR_RAW_ARRAY); 
+0

эй большое спасибо! есть ли способ оптимизации там, удалив элементы в массиве RAW, которые уже были нажаты? –

+0

Я действительно смотрел на попытку «сращить» фрагменты входного массива во время обработки, но поскольку обе функции используют указатели обратно в 'inputArray', которые действительно испортили бы итератор, пытаясь пропустить каждый элемент. Кроме того, глядя на перфорирование сращивания разных массивов, чтобы перейти к функции 'findChildren', вы, возможно, не лучше, так как это может иметь некоторые накладные расходы. Наконец, проверка свойства '.length' массива немного замедляет работу (особенно на больших массивах). Гораздо лучше сначала кэшировать его в переменной, а затем цикл против этого. – ericb

0

Когда вы создаете «функцию строителя дерева», вам нужно решить, является ли «элемент верхнего уровня» единственным элементом или массивом элементов. Поскольку вы сказали itemS, мы идем с массивом. Разница - это параметр, который вы передаете и возвращаетесь обратно, если его массив передается parentId, иначе мы передаем идентификатор.

function buildTree(parentId, list) { 
    var nodes = []; 
    for (var i=0, l; l = list[i]; i++) { 
    if (l.parentId === parentId) { 
// if you need "myList" intact afterwards remove the next line at the cost of efficiency 
     list.splice(i, 1); i--; 
     nodes.push({ 
     id: l.id 
     ,parentId: l.parentId 
     ,name: l.name 
     ,children: buildTree(l.id, list) 
     }); 
    } 
    } 
    return nodes; 
} 

var myTree = buildTree(null, myList); 
+0

Единственная серьезная проблема, с которой я сталкиваюсь в рекурсивном решении (точнее, рекурсивных решениях, о которых я видел и думал), заключается в том, что это было бы безумно медленным для большого количества узлов. Каждому неучтенному узлу потребуется сканирование всего массива для его дочерних элементов, что делает алгоритм O (n^2) - или, что еще хуже, если массивные нажатия не будут постоянными. Это концептуально проще, но это не имеет большого значения, если требуется целый день для запуска. :) – cHao

+0

Исправлено - удалите элементы по мере их нахождения - ну это лучше, но все же не сверхбыстро. – James

+0

К сожалению, сплайсинг AFAIK - это операция O (n). Для действительно больших массивов это может фактически замедлить его, поскольку теперь вы потенциально находитесь в O (n^3). – cHao

 Смежные вопросы

  • Нет связанных вопросов^_^