【问题标题】:Javascript recursive nested comparison loopJavascript递归嵌套比较循环
【发布时间】:2018-11-11 01:46:52
【问题描述】:

我正在尝试创建一个循环遍历配置对象的递归函数,该对象包含必须设置的键,这与稍后的 appConfig 进行比较,后者必须匹配 @ 内的所有键(嵌套) 987654323@

const _ = window._
const UNSET_CONFIG_VAR = 'UNSET'

let config = {
  logging: {
    hasConsole: UNSET_CONFIG_VAR,
    hasSyslog: UNSET_CONFIG_VAR,
    processName: UNSET_CONFIG_VAR,
    inner: {
      test: UNSET_CONFIG_VAR
    }
  }
}

const unsetConfig = _.clone(config)

function initialize (appConfig) {
  const walkConfig = (currentConfigLevel, previousPaths = []) => {
    for (const [key, value] of Object.entries(currentConfigLevel)) {
      if (_.isObject(value)) {
        previousPaths.push(key)
        walkConfig(currentConfigLevel[key], previousPaths)
      }

      const path = previousPaths.length !== 0 ? previousPaths.join('.') : key

      if (!_.get(appConfig, path)) {
        throw new Error(`Config was missing: ${path}`)
      }
    }
  }

  walkConfig(unsetConfig)
}

initialize({
  logging: {
    hasConsole: true,
    hasSyslog: true,
    processName: true,
    inner: {
      as: true
    }
  }
})
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>

预期的输出是会引发错误,正如您在 initialize 函数中看到的那样,logging.inner 中缺少 test 键。

但是没有抛出错误。

我现在已经在不同的设置中尝试过了,但我有点卡住了。

【问题讨论】:

标签: javascript loops object recursion lodash


【解决方案1】:

如果您只想检查某个路径中的值是否存在,那么您只需要使用lodash#has。使用lodash#get 会从某个路径获取特定属性的值,但是使用该值来检查该路径是否存在并不可靠,因为该值可能是假值。

此外,lodash#haslodash#get 都接受的路径参数接受字符串或数组,因此您实际上不需要从分隔符连接它们。实际上,当您的配置属性之一包含数组时,以这种方式加入它会失败。由于数组键符号包含在括号中,例如[0][1]

function initialize(appConfig) {

  const walkConfig = (config, paths = []) => {
    Object.entries(config).forEach(([key, value]) => {
      let $paths = paths.concat(key);

      if(!_.has(appConfig, $paths)) {
        throw new Error(`Config was missing: ${$paths.join('.')}`);
      }

      if(_.isObject(value)) {
        walkConfig(value, $paths);
      }
    });
  };

  walkConfig(unsetConfig);

}

const { _ } = window;
const UNSET_CONFIG_VAR = 'UNSET'

let config = {
  logging: {
    hasConsole: UNSET_CONFIG_VAR,
    hasSyslog: UNSET_CONFIG_VAR,
    processName: UNSET_CONFIG_VAR,
    inner: {
      test: UNSET_CONFIG_VAR
    }
  }
}

const unsetConfig = _.cloneDeep(config); // Clone inner objects as well

function initialize(appConfig) {
  
  const walkConfig = (config, paths = []) => {
    Object.entries(config).forEach(([key, value]) => {
      let $paths = paths.concat(key);
      
      if(!_.has(appConfig, $paths)) {
        throw new Error(`Config was missing: ${$paths.join('.')}`);
      }
      
      if(_.isObject(value)) {
        walkConfig(value, $paths);
      }
    });
  };
  
  walkConfig(unsetConfig);
  
}

initialize({
  logging: {
    hasConsole: false,
    hasSyslog: true,
    processName: true,
    inner: {
      as: true
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

【讨论】:

  • Error: Config was missing: logging.hasConsole.hasSyslog - 失败?
  • 我也会交换 if(_.isObject(value)) {if(!_.has(appConfig, $paths)) { 的顺序 - 以获得正确的错误消息:p
  • 我很确定错误消息与 OP 提到的一样正确:The expected output would be that an error would be thrown, as you can see in the initialize function the test key is missing in logging.inner.
  • 是的,但是尝试将Inner: { as: true} 甚至Inner: {test: true} 代替inner: {as:true} 放在传递给初始化的对象中......当然,错误在某种程度上是“正确的”,但是事实上inner 是不存在的属性
  • 我认为我们之间已经很好地回答了:p 我会删除我的答案,它基本上是多余的:p
猜你喜欢
  • 1970-01-01
  • 2014-09-14
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多