[英]Convert a Single Dimension Array to Two Dimensional Array with DAG Objects
我們正在構建一個應用程序,客戶端將能夠在其中構建工作流,表示為DAG 。 當在屏幕上輸出時,它將如下所示:
上圖使用二維數組輸出到屏幕,模型如下所示:
const dag = [
[
{ id: 1, parentId: 0 }
],
[
{ id: 2, parentId: 1 },
{ id: 3, parentId: 1 }
],
[
{ id: 6, parentId: 2 },
{ id: 4, parentId: 3 },
{ id: 5, parentId: 3 }
],
]
二維數組模型允許我遍歷它並在屏幕上輸出每一行; 所以在第一項dag
模型是第一行與一個節點,在第二項dag
模型是在第二排具有兩個節點,依此類推。
這部分有效,我什至想出了如何動態地將節點添加到圖形中。 我遇到問題的部分是從一維數組創建二維數組。 圖的數據將發送到服務器並作為一維數組從服務器返回,如下所示:
const items = [
{ id: 1, parentId: 0 },
{ id: 2, parentId: 1 },
{ id: 3, parentId: 1 },
{ id: 4, parentId: 3 },
{ id: 5, parentId: 3 },
{ id: 6, parentId: 2 },
]
我需要將這個items
數組轉換成上面的dag
多維數組。
這是我到目前為止所擁有的:
// this is the end array, (two dimensions [][]), that will be used to draw the graph
const dag = [];
// this will create the first row of the array, with all the items that have a parentId of 0
const newRow = items
.filter((item) => item.parentId === 0)
.reduce((arr, item) => {
arr.push(item);
return arr;
}, []);
dag.push(newRow);
// Now I'm trying to move down the graph from top to bottom
dag.forEach((row) => {
// get the list of item IDs that are in this row
const itemIdsInRow = row.reduce((prev, row) => {
prev.push(row.id);
return prev;
}, []);
// use the itemIdsInRow to get the items from the single dimensional array that will go in the next row
const itemsForNextRow = itemIdsInRow.reduce((itemsArr, id) => {
const nextItems = items.filter((item) => item.parentId === id);
itemsArr.push(...nextItems);
return itemsArr;
}, []);
// push the new row onto the two dimension array
dag.push(itemsForNextRow);
});
這適用於dag
的前兩行。 這說得通; 當我繼續使用dag
模型時, forEach
不允許我繼續通過循環來構建第三行。 如果我在前兩行的forEach
之前啟動dag
數組,我可以正確構建第三行,因此構建dag
的算法非常接近那里。
如果您對如何解決這個問題有任何意見或想法,我將不勝感激。 謝謝!
我們也有必要現在每個項目的parentId
是數組parentId
秒。 這是因為兩個分支可以在一個點合並,所以一個節點可能有兩個(或更多)父節點。 想象在上圖中,節點 4 和 5 在它們下面有一個子節點,它們每個都連接到該子節點。 這是代碼中的起始數組和結果數組:
const start = [
{ isYesPath: undefined, name: 'Step 1', parentIds: [0], stepId: 1, type: DagBranchType.Normal },
{ isYesPath: undefined, name: 'Step 2', parentIds: [1], stepId: 2, type: DagBranchType.IfElse },
{ isYesPath: true, name: 'Step 3', parentIds: [2], stepId: 3, type: DagBranchType.Normal },
{ isYesPath: false, name: 'Step 4', parentIds: [2], stepId: 4, type: DagBranchType.Normal },
{ isYesPath: undefined, name: 'Step 5', parentIds: [3, 4], stepId: 5, type: DagBranchType.Normal },
]
const result = [
[{ isYesPath: undefined, name: 'Step 1', parentIds: [0], stepId: 1, type: DagBranchType.Normal }],
[{ isYesPath: undefined, name: 'Step 2', parentIds: [1], stepId: 2, type: DagBranchType.IfElse }],
[
{ isYesPath: true, name: 'Step 3', parentIds: [2], stepId: 3, type: DagBranchType.Normal },
{ isYesPath: false, name: 'Step 4', parentIds: [2], stepId: 4, type: DagBranchType.Normal }
],
[{ isYesPath: undefined, name: 'Step 5', parentIds: [3, 4], stepId: 5, type: DagBranchType.Normal }]
]
您可以通過使用forEach
和filter
方法創建一個遞歸函數並傳遞父 ID 和當前級別來獲得所需的結果。 然后,您可以使用 level 按索引添加到結果數組中。
const items = [{"id":1,"parentId":0},{"id":2,"parentId":1},{"id":3,"parentId":1},{"id":4,"parentId":3},{"id":5,"parentId":3},{"id":6,"parentId":2}] const result = [] const modify = (data, pid = 0, level = 0) => data .filter(({ parentId }) => parentId === pid) .forEach(e => { if (!result[level]) result[level] = [e] else result[level].push(e); modify(data, e.id, level + 1) }) modify(items) console.log(result);
已經有幾個解決方案,但是我想建議一個沒有突變的解決方案。
const items = [ { id: 1, parentId: 0 }, { id: 2, parentId: 1 }, { id: 3, parentId: 1 }, { id: 4, parentId: 3 }, { id: 5, parentId: 3 }, { id: 6, parentId: 2 }, ]; const partition = predicate => list => [ list.filter (predicate), list.filter (x => !predicate (x)), ]; const inLevel = nth => original => item => { if (nth < 0) return false; const parent = original.find (({id}) => id === item.parentId); return !parent ? nth === 0 : inLevel (nth - 1) (original) (parent); }; const buildTree = (original) => (list, nth = 0) => { const [currentLevel, rest] = partition (inLevel (nth) (original)) (list); return rest.length > 0 ? [ currentLevel, ...buildTree (original) (rest, nth + 1) ] : [ currentLevel ]; } console.log (buildTree (items) (items));
一種可能的解決方案如下。 我不確定這是最好的方法還是最有效的方法,但它確實有效。 基本上,我會跟蹤items
數組中的哪些對象已被使用,當它們使用時,我將它們推送到usedItems
數組。 我循環了將新行添加到dag
模型的代碼,而usedItems.length !== items.length
。
const items = [
{ id: 1, parentId: 0 },
{ id: 2, parentId: 1 },
{ id: 3, parentId: 1 },
{ id: 4, parentId: 3 },
{ id: 5, parentId: 3 },
{ id: 6, parentId: 2 },
];
// As items are used, push them on this array
const usedItems = [];
// this is the end array, (two dimensions [][]), that will be used to draw the graph
const dag = [];
// this will create the first row of the array, with all the items that have a parentId of 0
const newRow = items
.filter((item) => item.parentId === 0)
.reduce((arr, item) => {
arr.push(item);
return arr;
}, []);
dag.push(newRow);
usedItems.push(...newRow);
// go until the usedItems array is the same length as the original items array
while (usedItems.length !== items.length) {
// get the list of item IDs that are in this row
const row = dag[dag.length - 1];
const itemIdsInRow = row.reduce((prev, row) => {
prev.push(row.id);
return prev;
}, []);
// use the itemIdsInRow to get the items from the single dimensional array that will go in the next row
const itemsForNextRow = itemIdsInRow.reduce((itemsArr, id) => {
const nextItems = items.filter((item) => item.parentId === id);
itemsArr.push(...nextItems);
return itemsArr;
}, []);
// push the new row onto the two dimension array
dag.push(itemsForNextRow);
usedItems.push(...itemsForNextRow);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.