У меня есть необходимость изменить структуру вложенного массива объектов, которая всегда имеет один и тот же шаблон, зная только индексы для доступа к тому конкретному объекту, который я хочу изменить.
Пример массива JSON:
[
{
"name":"bob",
"age":5,
"subRows":[
{
"name":"paul",
"age":10,
"subRows":[]
},
{
"name":"claire",
"age":20,
"subRows":[
{
"name":"carl",
"age":40,
"subRows":[]
}
]
}
}
]
Мне нужно динамически удалить объект, который каждый раз может быть другим, скажем, с именем "carl", поэтому для достижения ожидаемого результата:
[
{
"name":"bob",
"age":5,
"subRows":[
{
"name":"paul",
"age":10,
"subRows":[]
},
{
"name":"claire",
"age":20,
"subRows":[]
}
}
]
Зная все индексы структуры Json для доступа к ней:
let indexesArray = [0, 1, 0];
Я пробовал это:
const deleteObject = (jsonData, indexesArray) =>{
let pathToDelete = "jsonData[indexesArray[0]]";
for(let i=1; i<indexesArray.length(); i++){
pathToDelete += ".subRows["i"]";
]
pathToDelete = []; //empty the array removing "carl" object
return jsonData;
}
Как я могу заставить javascript обрабатывать мою переменную pathTodelete, созданную динамически, для удаления определенного объекта? Есть ли лучшие способы сделать это (конечно, мой код не работает)?
Вот одна версия, немного более общая, позволяющая нам динамически указывать имя subRow
для создания функции, которая делает это:
const removeIndexPath = (subName) => (xs, [i, ...is]) =>
i === undefined
? xs
: is.length === 0
? [... xs .slice (0, i), ... xs .slice (i + 1)] // skip xs[i]
: [
... xs .slice (0, i),
... (i in xs // replace xs[i] by recurring with the remaining path on subName
? [{...xs[i], [subName]: removeIndexPath (subName) (xs [i] [subName] || [], is)}]
: [] // handle missing nodes
),
... xs .slice (i + 1)
]
const removeSubRowPath = removeIndexPath ('subRows')
const input = [{name: "bob", age: 5, subRows: [{name: "paul", age: 10, subRows: []}, {name: "claire", age: 20, subRows: [{name: "carl", age: 40, subRows: []}]}]}]
console .log ('without carl:', removeSubRowPath (input, [0, 1, 0]))
console .log ('without clair:', removeSubRowPath (input, [0, 1]))
console .log ('without paul:', removeSubRowPath (input, [0, 0]))
console .log ('without bob:', removeSubRowPath (input, [0]))
console .log ('without nonexistent node:', removeSubRowPath (input, [0, 3, 5]))
.as-console-wrapper {max-height: 100% !important; top: 0}
Он обрабатывает узлы, у которых нет свойства subRow
(или любого другого), и обрабатывает попытки удалить несуществующие индексы в любом месте пути, просто возвращая копию оригинала.
Интересно, является ли это основной целью? Если это шаг, предпринятый после того, как вы впервые нашли путь к «carl», вам может быть лучше написать специальную функцию, которая удаляет вложенный узел, соответствующий предоставленному предикату (возможно, ({name}) => name == "carl"
или что-то в этом роде). Если это ваша настоящая цель, пожалуйста, начните другой вопрос (и не стесняйтесь добавлять ссылку на него в комментариях здесь.)
Вы можете просто использовать метод reduce
для массива индексов, и когда будет найден последний индекс и совпадение, вы можете удалить этот элемент из массива по индексу, используя метод splice
.
const data = [{"name":"bob","age":5,"subRows":[{"name":"paul","age":10,"subRows":[]},{"name":"claire","age":20,"subRows":[{"name":"carl","age":40,"subRows":[]}]}]}]
let indexesArray = [0, 1, 0];
indexesArray.reduce((r, e, i, a) => {
const match = r[e];
if (a.length == i + 1 && match) r.splice(e, 1)
else if (match) return match.subRows;
return {}
}, data)
console.info(data)
Поскольку вы отметили вопрос как «рекурсия», вот простая рекурсия:
function f(arr, path){
function g(i, curr){
if (i == path.length - 1){
curr.subRows.splice(path[i], 1);
return arr;
} else {
return g(i + 1, curr.subRows[path[i]]);
}
}
return g(1, arr[path[0]]);
}
var data = [{"name":"bob","age":5,"subRows":[{"name":"paul","age":10,"subRows":[]},{"name":"claire","age":20,"subRows":[{"name":"carl","age":40,"subRows":[]}]}]}];
var path = [0, 1, 0];
console.info(f(data, path));
Вот итеративное решение с использованием объектного сканирования
Сначала создаем путь, затем склеиваем найденную сущность
// const objectScan = require('object-scan');
const myData = [{ name: 'bob', age: 5, subRows: [ { name: 'paul', age: 10, subRows: [] }, { name: 'claire', age: 20, subRows: [{ name: 'carl', age: 40, subRows: [] }] } ] }];
const prune = (data, indices) => objectScan(
[indices.map((e) => `[${e}]`).join('.subRows')],
{
rtn: 'bool',
abort: true,
filterFn: ({ property, parent }) => parent.splice(property, 1)
}
)(data);
console.info(prune(myData, [0, 1, 0])); // returns true iff deleted
// => true
console.info(myData);
// => [ { name: 'bob', age: 5, subRows: [ { name: 'paul', age: 10, subRows: [] }, { name: 'claire', age: 20, subRows: [] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src = "https://bundle.run/[email protected]"></script>
Отказ от ответственности: я автор object-scan