Изменить n-е вложенное свойство в объекте

#javascript #object #recursion #nested #logic

#javascript #объект #рекурсия #вложенный #Логические

Вопрос:

Это в основном логический вопрос, который должен применяться на любом языке программирования, я просто использую JavaScript для этого

В принципе, представьте, что у меня есть какой-то объект, подобный

 {Props:"stuff", 
    other: {
        stuff: "etc",
        other: {
            Extra: "filler",
            other: {}
        }
    }
}

  

По сути, это объект с некоторым неизвестным количеством дополнительных свойств, но каждый подобъект имеет одно вложенное свойство с тем же именем «(в данном случае «другое»), и глубина может быть бесконечной, что означает, что внутри корневого объекта может быть n объектов, причем каждый подобъект является контейнером внутри другого объекта, ключ которого тот же (опять же, в этом случае «другой»)

Это может показаться более сложным, чем есть на самом деле, но в основном свойство other вложено и повторяется внутри корневого объекта

Дело в том, что я хочу создать функцию для разрыва этой вложенной цепочки с заданной глубиной (n), тем самым установив для всех дальнейших вложенных объектов одно значение

Это могло показаться сложным, но в основном, в приведенном выше примере объект имеет вложенную глубину свойства «other», равную 3, допустим, я хочу разбить глубину на индекс глубины 2, тем самым изменив объект, чтобы иметь только одно вложенное свойство «other», установленное на некоторое значение (например, число или строку, скажем, «5» например), Вместо того, чтобы быть установленным на еще один объект, который также содержит другое свойство (а также потенциально дополнительные свойства и т.д.)

Итак, как я могу создать функцию, которая принимает базовый объект, содержащий неизвестное количество вложенных свойств Ruth key k , и заставить его возвращать новый объект (или изменять оригинал, но я бы предпочел просто вернуть новый), который почти идентичен базовому объекту, за исключением того, что при индексе глубины n вложенному свойству с ключом k будет присвоено значение v , вместо того, чтобы продолжать его цепочку с неизвестным дополнительным количеством вложенной глубины

Я даже не могу понять, как это сделать, я всегда просто возвращаюсь к настройке свойства с индексом, k равным корневой функции, но передаю само свойство в качестве параметра вместо исходного корневого объекта (в основном рекурсия), за исключением случаев, когда входной объект не содержит свойства k , и в этом случае возвращается только само свойство, но это всего лишь рекурсивный метод для возврата самого внутреннего вложенного свойства, но в недоумении, что делать для достижения вышеупомянутого результата, который, опять же, является новым объектом почти идентичен базовому объекту, за исключением того, что для его вложенного свойства с ключом k на глубине n установлено значение v , я просто в полной растерянности относительно того, с чего вообще начать

Ответ №1:

Мы можем использовать рекурсивную функцию здесь, например, так

 let obj = {Props:"stuff", 
    other: {
        stuff: "etc",
        other: {
            Extra: "filler",
            other: {
                stuff: "abc",
                other:{
                    stuff: "acbh",
                    other: {}
                }
            }
        }
    }
};

function normalizeObjectDepth(obj, level, curr_level, key){
    if(curr_level==level-1){
        obj.other = key;
        return;
    }
    normalizeObjectDepth(obj.other, level, curr_level 1, key);
}

normalizeObjectDepth(obj, 3, 0, 5);

console.log(obj);  

Ответ №2:

Это гарантирует глубокое клонирование, поэтому исходный объект не изменяется. Единственная часть «высокого уровня» — это JSON.parse(JSON.stringify(…)) , но ее можно заменить любым методом глубокого копирования, или вы можете просто выполнить глубокое копирование перед использованием функции

 const modifyNthKey = (obj, key, value, n) => {
  let i = 0
  let newObj = JSON.parse(JSON.stringify(obj))
  let tmpObj = newObj
  while (i < n) {
    console.log('typeof: '   typeof tmpObj[`${key}`])
    if (typeof tmpObj[`${key}`] !== 'object') {
      throw Error('unable to access key. Parent not object')
    }
    tmpObj = tmpObj[`${key}`]
    i  
  }

  tmpObj[`${key}`] = value
  return newObj
}
  

Ответ №3:

Вы могли бы создать рекурсивную функцию с reduce методом, который будет проверять, меньше ли текущий уровень рекурсии, чем целевой, и на основе этого продолжить рекурсию или установить желаемое значение.

 const data = {
  Props: "stuff",
  other: {
    stuff: "etc",
    other: {
      Extra: "filler",
      other: {}
    }
  }
}

function modify(obj, key, lvl, val, clvl = 0) {
  return Object.entries(obj).reduce((r, [k, v]) => {
    if (k === key) {
      if (clvl < lvl) {
        r[k] = modify(v, key, lvl, val, clvl   1)
      } else {
        r[k] = val
      }
    } else {
      r[k] = v;
    }

    return r;
  }, {})
}

const result = modify(data, 'other', 2, 'my value');
console.log(result)  

Решение с простым for...in циклом.

 const data = {
  Props: "stuff",
  other: {
    stuff: "etc",
    other: {
      Extra: "filler",
      other: {
        random: 'foo',
        other: 'random'
      }
    }
  }
}

function modify(obj, key, lvl, val, clvl = 0) {
  const result = {}

  for (let k in obj) {
    if (k === key) {
      if (clvl < lvl) {
        result[k] = modify(obj[k], key, lvl, val, clvl   1)
      } else {
        result[k] = val
      }
    } else {
      result[k] = obj[k]
    }
  }

  return result

}

const result = modify(data, 'other', 2, 'random');
console.log(result)  

Комментарии:

1. Спасибо, хотя я должен был упомянуть, что я хочу сохранить этот как можно более низкий уровень, не используя никаких готовых методов, включая reduce, просто basic for циклы, приращения переменных и вызовы функций по существу

2. @bluejayke Я обновил свой ответ с помощью for...in решения цикла

Ответ №4:

Вот довольно простая рекурсивная версия. Это не изменяет ваш ввод, но возвращает новую структуру:

 const modify = (obj, key, value, depth) => ({
  ...obj, 
  [key]: depth <= 0 ? value : modify (obj [key], key, value, depth - 1)
})

const data = {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other: {}}}}

console .log (modify (data, 'other', 'new value', 2))
//~> {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other:"new value"}}}

console .log (modify (data, 'other', 'new value', 1))
//~> {Props: "stuff", other: {stuff: "etc", other: "new value"}}

console .log (modify (data, 'other', 'new value', 0))
//~> {Props: "stuff", other: "new value"}  
 .as-console-wrapper {max-height: 100% !important; top: 0}  

По сути, мы просто копируем объект, устанавливая целевому свойству ( 'other' ) либо новое значение (если мы находимся в конце рекурсии, когда depth равно 0), либо результат рекурсивного вызова с использованием object[key] и depth -1 (в противном случае.)

Обратите внимание, что мы не делаем полный клон, поэтому другие узлы могут быть разделены по ссылке.