Javascript: получить доступ к члену объекта (ассоциативного массива) на любой глубине при условии, что промежуточные члены могут отсутствовать

Допустим, необходимо получить доступ к свойству:

data.a.b.c.d.e.f

Или получить undefined, если такое свойство отсутствует (в том числе по причине отсутствия промежуточных членов объекта data, например при отсутствии a, b, c и т.д.).

Если использовать предусмотренную для этого конструкцию ?., то получаются такие вещи:

data?.a?.b?.c?.d?.e?.f

Вопрос:

Можно ли запись сделать более короткой и понятной, чем у каждого члена ставить ?.?


Ответы (2 шт):

Автор решения: EzioMercer

Как возможный вариант, могу предложить простую реализацию метода get с помощью try..catch:

const get = (obj, propsPath) => {
  try {
    const paths = propsPath.split('.');
    let result = obj;
    
    for (const path of paths) {
      result = result[path];
    }
    
    return result;    
  } catch (e) {        
    return undefined;
  }
}

const data = {
  a: {
    b: {
      c: {
        d: {
          e: {
            f: 45
          }
        }
      }
    }
  }
}

const results = [
  get(data, ''),
  get(data, 'a'),
  get(data, 'a.b'),
  get(data, 'a.b.c'),
  get(data, 'a.b.c.d'),
  get(data, 'a.b.c.d.e'),
  get(data, 'a.b.c.d.e.f'),
  get(data, 'a.b.c.d.e.f.g'),
  get(data, 'a.b.c.d.e.f.g.h'),
  get(data, 'a.b.c.d.e.f.g.h.i'),
].map(result => result === undefined ? result : JSON.parse(JSON.stringify(result)));

console.log(results);

P.S. Такая простая реализцаия не будет отличать существование поля с значением undefined и отсутствием самого поля вообще, но это легко исправить

→ Ссылка
Автор решения: Stanislav Volodarskiy

Эксперимент с использованием Proxy. К сожалению, я не сумел возвращать undefined, вместо него undefinedProxy, что снижает уровень красивости решения. Но изменить поведение . можно:

const isDict = v => v !== undefined && v !== null && v.constructor === Object;

const undefinedProxy = new Proxy({}, {
    get(target, prop, receiver) {
        return undefinedProxy;
    }
});

const makeProxy = data => new Proxy(data, {
    get(target, prop, receiver) {
        if (prop in target) {
            const value = target[prop];
            return isDict(value) ? makeProxy(value) : value;
        }
        return undefinedProxy;
    }
});

const data = {a: {b: {z: 42}}};
const p = makeProxy(data);

console.log('data', data);
console.log('p', p);
console.log('p.a', p.a);
console.log('p.a.b', p.a.b);
console.log('p.a.b.c', p.a.b.c);
console.log('p.a.b.c.d', p.a.b.c.d);
console.log('p.a.b.c.d.e', p.a.b.c.d.e);
console.log('p.a.b.c.d.e.f', p.a.b.c.d.e.f);
console.log('p.a.b.c.d.e.f === undefinedProxy', p.a.b.c.d.e.f === undefinedProxy);
console.log('p.a.b.z', p.a.b.z);

→ Ссылка