创建一个 'object accessor' 函数 - 深度嵌套 javascript 个对象

Creating an 'object accessor' function - Deeply nested javascript objects

动机:给定一个深度嵌套的对象,在第N.However层有'currency'个键,N层可能不统一,这意味着对象A的货币键可能位于第4层,并且同一个键可能位于对象 B 的第 9 层

我想要实现的是创建一个统一的访问器。仅生成一次并在遇到类似对象数组(深度嵌套)时使用的即时访问器。

目前,通过递归,我设法将 'accessor path' 构造为字符串。 我如何将其应用于我的对象? 尝试这样做是否合理?我非常感谢您的输入、想法和意见。

// testObj[level][id][title][name][currency] === 'USD'

const testObj = {
  level: {
    id: {
      title: {
        name: {
          currency: "USD"
        }
      }
    }
  }
}

// testObj2[a][b][c][currency]
const testObj2 = {
  a: {
    b: {
      c: {
        currency: "USD"
      }
    }
  }
}

const testObj3 = {
  a: {
    b: {
      c: {
        d: {
          e: {
            currency: "USD"
          }
        }
      }
    }
  }
}

const objectAccessCreator = (obj, targetKey, keysArray = []) => {
  for (const [key, value] of Object.entries(obj)) {
    if (key !== targetKey) {
      keysArray.push(key);

      if (typeof value !== 'object' || typeof value === null) {
        return -1;
      } else {
        return objectAccessCreator(value, 'currency', keysArray);
      }
    }
    if (key === targetKey) {
      keysArray.push(key);
    }
  }

  let accessChain = '';

  for (const key of keysArray) {
    accessChain += `[${key}]`;
  }
  return accessChain;
}

const targetKey = 'currency';

// [a][b][c][d][e][currency]
console.log(objectAccessCreator(testObj3, targetKey));

//[level][id][title][name][currency]
console.log(objectAccessCreator(testObj, targetKey));


//[a][b][c][currency]
console.log(objectAccessCreator(testObj2, targetKey));

您可以将字符串拆分为路径数组,然后使用 Array.reduce() 到 return 属性 值。

我将其包装在一个 getValue() 函数中,该函数接受一个对象和路径参数。

const objectAccessCreator = (obj, targetKey, keysArray = []) => {
  for (const [key, value] of Object.entries(obj)) {
    if (key !== targetKey) {
      keysArray.push(key);

      if (typeof value !== 'object' || typeof value === null) {
        return -1;
      } else {
        return objectAccessCreator(value, 'currency', keysArray);
      }
    }
    if (key === targetKey) {
      keysArray.push(key);
    }
  }

  let accessChain = '';

  for (const key of keysArray) {
    accessChain += `[${key}]`;
  }
  return accessChain;
}

const targetKey = 'currency';

function getValue(obj, path) {
    return path.split(/[\[\]]+/).filter(s => s).reduce((obj, key) => { 
        return obj[key];
    }, obj);
}

const testObjects = [
  { level: { id: { title: { name: { currency: "USD" } } } } },
  { a: { b: { c: { currency: "USD" } } } },
  { a: { b: { c: { d: { e: { currency: "USD" } } } } } }
];
  
for(let testObj of testObjects) { 
    const path = objectAccessCreator(testObj, targetKey);
    console.log('Generated path:', path);
    console.log('Value at path:', getValue(testObj, path));
}
    
.as-console-wrapper { max-height: 100% !important; }

我还建议使用“.”。路径分隔符,这在一定程度上简化了逻辑:

const objectAccessCreator = (obj, targetKey, keysArray = []) => {
  for (const [key, value] of Object.entries(obj)) {
    if (key !== targetKey) {
      keysArray.push(key);

      if (typeof value !== 'object' || typeof value === null) {
        return -1;
      } else {
        return objectAccessCreator(value, 'currency', keysArray);
      }
    }
    if (key === targetKey) {
      keysArray.push(key);
    }
  }

  return keysArray.join('.');
}

const targetKey = 'currency';

function getValue(obj, path) {
    return path.split('.').reduce((obj, key) => { 
        return obj[key];
    }, obj);
}

const testObjects = [
  { level: { id: { title: { name: { currency: "USD" } } } } },
  { a: { b: { c: { currency: "USD" } } } },
  { a: { b: { c: { d: { e: { currency: "USD" } } } } } }
];
  
for(let testObj of testObjects) { 
    const path = objectAccessCreator(testObj, targetKey);
    console.log('Generated path:', path);
    console.log('Value at path:', getValue(testObj, path));
}
    
.as-console-wrapper { max-height: 100% !important; }

以及直接从新的 getValue() 函数 return 获取 targetKey 值的示例:

const targetKey = 'currency';

function getValue(obj, property) {
    for(let key in obj) {
        if (obj[key] && (typeof(obj[key]) === 'object')) {
            let value = getValue(obj[key], property);
            if (value !== null) {
                return value;
            }
        } else if (key === property) {
            return obj[key];
        }
    }
    return null;
}

const testObjects = [
  { level: { id: { title: { name: { currency: "USD" } } } } },
  { a: { b: { c: { currency: "USD" } } } },
  { a: { b: { c: { d: { e: { currency: "USD" } } } } } }
];
  
for(let testObj of testObjects) { 
    console.log(`Value of "${targetKey}":`, getValue(testObj, targetKey));
}
    
.as-console-wrapper { max-height: 100% !important; }

一种稍微简化的方法。 retrievePathAndValue returns 路径及其在小对象中的值 ({path, value,})。如果您想稍后从结果路径中检索值,您可以使用 [result].path 和一些函数,例如所选答案中的函数或后者的 like this (see example

const  [ testObj, testObj2, testObj3 ] = testData();
const targetKey = 'currency';

console.log(retrievePathAndValue(testObj, targetKey));
console.log(retrievePathAndValue(testObj2, targetKey));
console.log(retrievePathAndValue(testObj3, targetKey));

function retrievePathAndValue(obj, key, path = ``) {
  for (let k of Object.keys(obj)) {
    if (obj[key]) {
      return {
        path: `${path}${path.length < 1 ? `` : `.`}${key}`,
        value: obj[key],
      };
    }

    if (obj[k] instanceof Object && !Array.isArray(obj[k])) {
      return retrievePathAndValue(
        obj[k], 
        key, 
        `${path}${path.length < 1 ? `` : `.`}${k}` );
    }
  }
  
  return {
    path: `[${key}] NOT FOUND`,
    value: undefined, 
  };
}

function testData() {
  const testObj = {
    level: {
      id: {
        title: {
          name: {
            currency: "USD"
          }
        }
      }
    }
  }

  // testObj2[a][b][c][currency]
  const testObj2 = {
    a: {
      b: {
        c: {
          currency: "USD"
        }
      }
    }
  }

  const testObj3 = {
    a: {
      b: {
        c: {
          d: {
            e: {
              currency: "USD"
            }
          }
        }
      }
    }
  }
  return [ testObj, testObj2, testObj3 ];
}
.as-console-wrapper {
    max-height: 100% !important;
}