#javascript #typescript #recursion #tree #traversal
Вопрос:
Я пытаюсь преобразовать список в дерево, используя параметры потомков и предков в элементе:
У меня, например, есть что-то вроде этого:
const itemToUpdate = {
id: 1,
ancestors: [
{
id: 2,
ancestors: [
{
id: 4
}
]
}
],
descendants: [
{
id: 3,
descendants: [
{
id: 5
},
{
id: 6,
descendants: [
{
id: 8
},
{
id: 9
}
]
},
{
id: 7
}
]
}
]
};
const updatedItem = {
id: 4,
children: [
{
id: 2,
children: [
{
id: 1,
children: [
{
id: 3,
children: [
{
id: 5
},
{
id: 6,
children: [
{
id: 8
},
{
id: 9
}
]
},
{
id: 7
}
]
}
]
}
]
}
]
};
Дело в том, что деревья могут быть вложены на многих уровнях. Кроме того, на уровне может быть много предков и потомков, поэтому мне нужно более общее решение. Дерево должно быть создано вверх от предков и вниз от потомков.
Я нашел способ получить потомков дерева, но мне все равно нужно добавить к этому предков на вершине этого дерева:
export const convertListToTree = <T extends ApiTree<T>>(list: Array<T>, key = 'ascendants'): Array<T> => {
const getNestedItem = (path: Array<string>, tree: Array<T>): T => {
const nestedItem = tree.find(nestedItem => nestedItem.id === path[0]);
const newPath = path.slice(1);
if (!newPath.length) {
return nestedItem;
} else if (nestedItem?.[key]) {
return getNestedItem(newPath, nestedItem?.[key]);
} else {
return getNestedItem(newPath, tree);
}
};
const sortListByPathLength = (list: Array<T>): Array<T> => {
return sortBy(list, (item: T) => item.path?.length);
};
const addRootItem = (item: T, tree: Array<T>): void => {
tree.push(item);
};
const addChildrenItem = (item: T, tree: Array<T>): void => {
const parent = getNestedItem(item.path, tree);
if (parent) {
parent[key].push(item);
} else {
addRootItem(item, tree);
}
};
const tree = [];
if (list?.length) {
const sortedList = sortListByPathLength(list);
sortedList.forEach(item => {
if (!item.path?.length) {
addRootItem(item, tree);
} else {
addChildrenItem(item, tree);
}
});
}
return tree;
};
Любые советы будут более чем приветствоваться. 🙏
Комментарии:
1. что делать, если у вас есть более одного предмета? что вы пробовали?
2. извините, что должно произойти, если у узла более одного предка? это действительно важно для решения.
3. @NinaScholz Хм, это хороший вопрос. Я думаю, что мы должны переместить один или несколько корневых уровней ниже, как в этой статье: shareablecode.com/snippets/…
4. Мне не ясно, что должно произойти с несколькими предками? Что тогда было бы корнем дерева? В моем понимании в этом случае нет дерева, а есть направленный ациклический граф. Пожалуйста, отредактируйте свой вопрос и приведите пример такого ввода с ожидаемым результатом.
5. @NinaScholz Ты совершенно прав, извини.. У каждого узла может быть несколько потомков, но только один предок. Я обновил пример с этой ситуацией. Извините за путаницу
Ответ №1:
Предполагая, что для каждого уровня существует только один предок, вы можете использовать рекурсивную функцию, которая сохраняет потомков, но возвращает самый ранний узел предка.
const
fn = ({ id, ancestors, descendants }, children) => {
const node = { id };
if (Array.isArray(children)) node.children = children;
if (descendants) node.children = descendants.map(fn);
return ancestors
? fn(ancestors[0], [node])
: node;
},
data = { id: 1, ancestors: [{ id: 2, ancestors: [{ id: 4 }] }], descendants: [{ id: 3, descendants: [{ id: 5 }] }] },
result = fn(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. Ух ты, круто. Мы должны предположить, что на каждом уровне может быть несколько потомков и предков. Но это уже кое-что 🙌
Ответ №2:
Я не совсем уверен, чего вы хотите там достичь. Не могли бы вы подробнее остановиться на этом?
Вот как вы бы преобразовали свой конкретный пример, если это поможет.
function convert(item) {
return {
id: item.ancestors[0].id,
children: [
{
id: item.id,
children: [
{
id: item.descendants[0].id
}
]
}
]
}
}
Комментарии:
1. Дело в том, что деревья могут быть вложены на многих уровнях. Кроме того, на уровне может быть много предков и потомков, поэтому мне нужно более общее решение. Дерево должно быть создано вверх от предков и вниз от потомков.
2. @Patryk, вы могли бы добавить этот случай к вопросу.
3. @Patryk не могли бы вы привести пример с большим количеством элементов в массивах, я не уверен, как вы хотите обрабатывать назначение идентификаторов, которые могут быть достаточно сложными