У меня есть массив с объектами, которые могут иметь дочерние элементы, дочерние элементы имеют ту же структуру, что и родитель, в основном это просто вложение объектов.
Мне интересно, как я могу удалить один из объектов по ключу. Например, я хочу удалить объект с id: 1 (который вложен в дочерний массив другого объекта)
const data = [
{
id: 2,
children: [
{
id: 1,
children: []
}
]
},
{
id: 3,
children: [],
}
]
Было бы возможно? Если объект с дочерними элементами удаляется, то дочерние элементы перемещаются в корень?
Я попытался переработать следующую функцию, которая возвращает все идентификаторы из моей структуры данных, поэтому она извлекает идентификатор и, если есть дочерние элементы, извлекает идентификаторы внутри этих дочерних элементов. Но как мне удалить объект с этим идентификатором?
export function flattenFindAttribute (data, attribute) {
return data.map(item => [item[attribute], ...flattenFindAttribute(item.children)]).flat()
}
Да, они взяты из базы данных.
Являются ли идентификаторы уникальными. Скажем, может ли идентификатор родителя совпадать с идентификатором ребенка?
Нет, идентификатор всегда будет уникальным на всех уровнях.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете сделать это следующим образом:
const data = [
{
id: 2,
children: [
{
id: 1,
children: []
}
]
},
{
id: 3,
children: [],
}
]
let deletedObj = {}
function deleteAtId(arr, deleteId) {
const rs = []
arr.forEach(({id, children}) => {
if (id !== deleteId) {
if (!children.length) {
rs.push({id, children})
} else {
const tmp = deleteAtId(children, deleteId)
rs.push({id, children: tmp})
}
} else deletedObj = {id, children}
})
return rs
}
const rs = [...deleteAtId(data, 1), {...deletedObj}]
console.info(rs)Спасибо! Забыл упомянуть, не будет ли сложно, если элемент с дочерними элементами будет удален, что дочерние элементы уйдут в корень?
@Notflip Я обновил ответ. Просто нужна глобальная переменная для хранения удаленного объекта, а затем добавить в окончательный массив.
Что это за $deletedObj, он находится вне функции?
Что-то вроде этого? Он повторяется рекурсивно и объединяется, когда находит объект с вашим идентификатором.
function removeObject(data, id){
data.forEach(point =>{
if (point.children.length > 0){
removeObject(point.children, id);
}
const index = data.findIndex(x => x.id == id);
if (index > -1){
data.splice(index ,1);
}
});
return data;
}
const data = [
{
id: 2,
children: [
{
id: 1,
children: []
}
]
},
{
id: 3,
children: [],
}
]
removeObject(data, 1);
console.info(data)Спасибо! Забыл упомянуть, не будет ли сложно, если элемент с дочерними элементами будет удален, что дочерние элементы уйдут в корень?
Вам просто нужно использовать рекурсивный вызов function и использовать Array#splice() метод для удаления искомого object.
Вот как должен быть ваш код:
function removeId(data, id) {
data.forEach((o, i) => {
if (o.id && o.id === id) {
data.splice(i, 1);
return true;
} else if (o.children) {
removeId(o.children, id);
}
});
}
Демо:
const data = [{
id: 2,
children: [{
id: 1,
children: []
}]
},
{
id: 3,
children: [],
}
];
function removeId(data, id) {
data.forEach((o, i) => {
if (o.id && o.id === id) {
data.splice(i, 1);
return true;
} else if (o.children) {
removeId(o.children, id);
}
});
}
removeId(data, 1);
console.info(data);Если вы хотите поместить весь удаленный элемент children в его родительский массив children, вам просто нужно передать третий параметр вашей функции, чтобы отслеживать родителя object:
function removeId(data, id, parent) {
data.forEach((o, i) => {
if (o.id && o.id === id) {
if (parent) {
o.children.forEach(c => parent.children.push(c));
}
data.splice(i, 1);
return true;
} else if (o.children) {
removeId(o.children, id, o);
}
});
}
Демо:
var data = [{
id: 2,
children: [{
id: 1,
children: [1, 2]
}]
},
{
id: 3,
children: [],
}
];
function removeId(data, id, parent) {
data.forEach((o, i) => {
if (o.id && o.id === id) {
if (parent) {
o.children.forEach(c=> parent.children.push(c));
}
data.splice(i, 1);
return true;
} else if (o.children) {
removeId(o.children, id, o);
}
});
}
removeId(data, 1);
console.info(data);Это работает, я забыл упомянуть, будет ли сложно, если элемент с детьми будет удален, чтобы дети ушли в корень?
@Notflip гул не сложный, но с учетом рекурсии это будет не так просто. потому что при вызове функции мы теряем контекст родителя.
Спасибо за ваш ответ, сейчас я пытаюсь реализовать его в вашей функции, но не думаю, что это будет легко
@Notflip На самом деле это может быть легко, нам просто нужно передать родителя object в function, проверьте мое редактирование для решения.
Возможно ли это и при использовании array.map? Так как я использую Vue, то пытаюсь добиться результата вашим методом, но таким, который возвращает, например: const newData = removeId(data, 1). Поскольку теперь он обновляет переданный объект данных, а не создает новый с результатом.
Ну, вы можете сделать это с обоими, вам просто нужно идеально обработать возвращаемый объект.
Спасибо за ваш быстрый ответ! Я пытался использовать array.map(), но он нарушает рекурсивность, возвращает undefined
Вы возвращаете объект на каждой итерации?
Исследуйте объект рекурсивно и удалите в соответствии с указанным предикатом:
const data =[...Array(4)].map(()=> [
{
id: 2,
children: [
{
id: 1,
children: []
}
]
},
{
id: 3,
children: [],
}
]);
function deleteObj(parent, predicate) {
if (predicate(parent)) {
return true;
}
if (typeof parent === 'object') {
if (Array.isArray(parent)) {
for (let i = 0; i < parent.length; i++) {
if (deleteObj(parent[i], predicate)) {
parent.splice(i, 1);
i--;
}
}
} else {
Object.keys(parent).forEach(key => deleteObj(parent[key], predicate) && delete parent[key]);
}
}
return false;
}
console.info('from array:', data[0]);
const test1 = data[1];
const test2 = data[2];
const test3 = data[3];
console.info('delete node with id === 1');
deleteObj(test1, node => node.id === 1);
console.info(test1);
console.info('delete node with id === 3');
deleteObj(test2, node => node.id === 3);
console.info(test2);
console.info('delete node with non empty children');
deleteObj(test3, node => node.children && node.children.length > 0);
console.info(test3);Эта рекурсивная функция работает для ваших нужд:
function rotateChildren(array, key) {
if (!array || !array.length) {
return array;
}
return array.filter(child => {
if (child) {
child.children = rotateChildren(child.children, key);
return child.id !== key;
}
return !!child;
});
}
const data = [
{
id: 2,
children: [
{
id: 1,
children: []
}
]
},
{
id: 3,
children: [],
}
];
console.info(rotateChildren(data, 1));
console.info(rotateChildren(data, 2));
console.info(rotateChildren(data, 3));Было бы возможно? Если объект с дочерними элементами удаляется, то дочерние элементы перемещаются в корень?
Вы можете добиться этого, используя Array.reduce с рекурсией для детей.
Пример:
const data = [
{ id: 1, children: [{ id: 2, children: [] }] },
{
id: 2,
children: [
{
id: 1,
children: [],
},
],
},
{
id: 3,
children: [],
},
{
id: 4,
children: [
{
id: 2,
children: [
{
id: 2,
children: [
{
id: 1,
children: [],
},
{
id: 2,
children: [],
},
],
},
],
},
],
},
];
function removeBy(data, predicate) {
return data.reduce((result, item) => {
if (!predicate(item.id)) {
const newItem = { ...item };
if (item.children.length > 0) {
newItem.children = removeBy(item.children, predicate);
}
result.push(newItem);
}
return result;
}, []);
}
const predicate = value => value === 1;
const result = removeBy(data, predicate);
console.info(result);
Ваши значения obj.id всегда уникальны для всех объектов?