Рекурсивный доступ к глубоко вложенному объекту

Постановка задачи

Мне нужен доступ к глубоко вложенный объект. Я мог бы использовать стороннюю утилиту, такую ​​как Лодаш но настоял на написании Ванильный JS решение с рекурсивная стратегия.

Код

Может ли это решение быть улучшен с лучше а также более эффективным подход? Есть ли край случай что было упущено и могло нарушить код?

const obj = {
  foo: {
    bar: {
      baz: "value"
    }
  }
};

const getPathValue = (object, path) => {  
  if (
    // ======= object checks ======= //
    object === null ||
    object === undefined ||
    typeof object !== "object" ||
    // ======= path checks ======= //
    path === null ||
    path === undefined ||
    path.length === 0
  ) {
    return undefined;
  }

  // key -> current key
  // rest -> rest of the keys in path
  const [key, ...rest] = path;

  // value of current key
  const value = object[key];

  // if rest of the keys are exhausted &
  // a value is found, return the value
  if (rest.length === 0 && value) {
    return value;
  }

  // as rest of key are not exhaused yet
  // in the path, keep traversing deeper
  return getPathValue(value, rest);
};

// ======= test cases with object edge cases ======= //
console.log(getPathValue()); // undefined
console.log(getPathValue(null)); // undefined
console.log(getPathValue(undefined)); // undefined

// ======= test cases with path edge cases ======= //
console.log(getPathValue(obj, [])); // undefined
console.log(getPathValue(obj, null)); // undefined
console.log(getPathValue(obj, [1, 2, 3])); // undefined
console.log(getPathValue(obj, ["non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "bar", "baz", "non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "non exisiting key", "bar", "baz"])); // undefined

// ======= normal test case ======= //
console.log(getPathValue(obj, ["foo", "bar", "baz"])); // value

1 ответ
1

Рекурсия

Есть много причин избегать рекурсии в JavaScript.

  • Рекурсия — неэффективный способ реализации простых циклов.

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

  • Сильный аргумент НИКОГДА не использовать рекурсию заключается в том, что JS имеет ограниченный стек вызовов, но тот же аргумент также означает, что НИКОГДА не вызывайте функцию (вы можете быть в конце стека вызовов), что является нелепым требованием.

    Пока JS не поддерживает оптимизацию хвостового вызова, вы всегда должны использовать цикл вместо рекурсии.

Проверка аргументов

Аргумент проверки слишком сложен и может быть упрощен до typeof object === "object" && object !== null && Array.isArray(path) см. переписать.

Примечание «null плохо» Хороший JS никогда не должен устанавливать значение null и, таким образом, избежать нелепой необходимости проверять, является ли объект null, к сожалению, мы должны иметь дело с разработчиками DOM и CSS, которые упускают из виду то, что null значит в JS.

Циклический сейф

Как правило, при повторении путей следует опасаться циклических путей, однако это вызывает беспокойство только в том случае, если поиск является открытым (может выполняться бесконечно) и, следовательно, не является проблемой в этом случае.

Непоследовательный возврат

Если передан пустой массив, ваш пример возвращает undefined что не имеет смысла по сравнению с неполным путем, который вернет Object или же value. Пустой путь эквивалентен неполному пути.

Например, объекты

const o1 = {A: { B: {C: 0}}};
const o2 = {B: {C: 0}};

Не удается найти корневой объект

getPathValue(o1, ["A"]);// returns {B: {C: 0}};
getPathValue(o1, []);   // returns undefined; One would expect {A: { B: {C: 0}}}
getPathValue(o2, []);   // returns undefined; One would expect {B: {C: 0}}
getPathValue(getPathValue(o1, ["A"]), []); // returns undefined; One would expect {B: {C: 0}}

Переписать

Переписать

  • Возврат undefined если путь не может быть найден.

  • Возвращает объект в конце пути. например getPathValue(obj, []) возвращается obj нет undefined

  • Также будет следовать индексному пути в массивы.

  • Использует ?? (Нулевой оператор объединения) чтобы упростить поиск по пути и, как таковой, вернет undefined скорее, чем null. Установка «null плохо» правило для JS.

  • Если второй аргумент не является массивом, результатом будет undefined.


function valueAtPath(obj, path) {  
    var i = 0;  
    if (typeof obj === "object" && obj !== null &&  Array.isArray(path)) {
        while (i < path.length && obj !== undefined) { obj = obj[path[i++]] ?? undefined }
        return obj;
    }
}

  • Имел o1 был доступен динамически с помощью o1[] или же o1[''], это вызвало бы ошибка или вернулся неопределенный значение соответственно. Итак, уважая такое поведение, я ожидаю getPathValue(o1, []) возвращаться неопределенный так же. Что вы думаете об этом?

    — Винай Шарма

  • @VinaySharma Массив с пустой строкой не является пустым массивом. Что вы вернете в этих случаях, зависит от вас, поскольку нет правильного или неправильного. Я считаю, что … Вы не предоставляете способ вернуть корневой объект и, следовательно, должны были бы добавить оператор к вызову, если бы вам потребовался корневой объект. Функция решает путь, если вы не делаете никаких шагов по пути, вы фактически остаетесь в начале (корне) пути. Если вы попытаетесь наступить на неопределенный путь, например, с именем "" пустая строка, значит, вы переместились в неопределенное место.

    — Слепой67

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *