изменение динамически вложенных массивов, зная только индексы для доступа

#javascript #arrays #json #recursion #ecmascript-6

#javascript #массивы #json #рекурсия #ecmascript-6

Вопрос:

У меня есть необходимость изменить структуру вложенного объекта-массива, которая всегда имеет один и тот же шаблон, зная только индексы для доступа к этому конкретному объекту, который я хочу изменить.

Пример массива 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, созданную динамически, для удаления определенного объекта? Есть ли лучшие способы сделать это (конечно, мой код не работает)?

Ответ №1:

Вы могли бы просто использовать 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 amp;amp; match) r.splice(e, 1)
  else if (match) return match.subRows;

  return {}
}, data)

console.log(data) 

Ответ №2:

Вот одна из версий, немного более общая, позволяющая нам 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" , или что-то подобное).) Если это ваша реальная цель, пожалуйста, задайте другой вопрос (и не стесняйтесь добавлять ссылку на него в комментариях здесь.)

Ответ №3:

Поскольку вы отметили вопрос «рекурсия», вот простая рекурсия:

 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.log(f(data, path)); 

Ответ №4:

Вот итеративное решение с использованием object-scan

Сначала мы создаем путь, затем мы объединяем найденный объект

 // 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.log(prune(myData, [0, 1, 0])); // returns true iff deleted
// => true

console.log(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/object-scan@13.8.0"></script> 

Отказ от ответственности: я автор object-scan