【问题标题】:Flatten JavaScript nested object展平 JavaScript 嵌套对象
【发布时间】:2020-12-22 06:56:15
【问题描述】:

我有一个嵌套对象,如下所示:

let obj = { 
    F:{
        asian: {
            "35-44": 1,
            "55-": 1,
        },
        "asian/black": {
            "0-24": 1,
            "35-44": 1,
            "45-54": 2,
        },
    },
    M:{
        asian: {
            "35-44": 1,
            "55-": 1,
        },
        white: {
            "0-24": 1,
            "35-44": 1,
            "45-54": 2,
        },
    },

}

我想将对象展平:

res = {
    F: 6,
    M: 6,
    asian: 4,
    "asian/black": 4,
    white: 4, 
    "0-24": 2,
    "35-44": 4,
    "45-54": 4,
    "55-": 2,
}

res中的每个值都应该是最深的对象值(F,M)和具有相同键的对象值(0-24,35-44...)的总和。我觉得这可以使用递归来完成,但无法做到。我写的代码:

let returnVal = 0
const flatten = (obj, prefix = '', res = {}) => {

  return Object.entries(obj).reduce((r, [key, val]) => {
    if(typeof val === 'object'){ 
      flatten(val, key, r)
    } else {
      res[key] = val
      returnVal = val;
    }

    if (key in res) {
        res[key] += returnVal
    } else {
        res[key] = 0
        res[key] += returnVal
    }
    
    return r
  }, res)
}
console.log(flatten(obj))

它会输出:

result = {
"0-24": 2,
"35-44": 2,
"45-54": 4,
"55-": 2,
F: 2,
M: 2,
asian: 2,
"asian/black": 2,
white: 2,
}

F、M 和其他一些键不正确。谢谢!

【问题讨论】:

  • just can't get it right - 请指出不正确的地方

标签: javascript object recursion nested


【解决方案1】:

另一种可能更简单的方法如下:

const consolidate = (obj, path = [], results = {}) =>
  Object .entries (obj) .reduce ((results, [k, v]) => 
    Object (v) === v 
      ? consolidate (v, [...path, k], results)
      : [...path, k] .reduce (
          (results, n) => ({...results, [n] : (results[n] || 0) + v}), 
          results
        ),
  results)

const data = {F: {asian: {"35-44": 1, "55-": 1}, "asian/black": {"0-24": 1, "35-44": 1, "45-54": 2}}, M: {asian: {"35-44": 1, "55-": 1}, white: {"0-24": 1, "35-44": 1, "45-54": 2}}}

console .log (consolidate (data))
.as-console-wrapper {min-height: 100% !important; top: 0}

我们递归地跟踪通过对象的路径,例如 ['F', 'asian/black', '45-54']['M', 'white'] 或简单的 ['f'] 以及包含最终结果的对象。当我们当前节点的值是一个对象时,我们递归,将当前属性名称添加到路径中。如果不是(因此,对于此数据,它必须命中一个数字),我们遇到一个基本情况,在这种情况下,我们获取当前路径中的每个节点,并通过将该数字添加到结果对象中节点的值来更新结果对象,或者如果该值不存在,则将其设置为当前值。

默认参数存在潜在问题,如another Q & A 中所述。如果有人试图在输入对象数组上直接使用map consolidate 函数,它将失败。如果这是一个问题,很容易将默认参数交换为包装函数:

const _consolidate = (obj, path, results) =>
  Object .entries (obj) .reduce ((results, [k, v]) => 
    Object (v) === v 
      ? _consolidate (v, [...path, k], results)
      : [...path, k] .reduce (
          (results, n) => ({...results, [n] : (results[n] || 0) + v}), 
          results
        ),
  results)

const consolidate = (obj) => 
  _consolidate (obj, [], {})

【讨论】:

  • 感谢您的代码!非常简洁优雅!
【解决方案2】:

const data = {
  F: {
    asian: {
      "35-44": 1,
      "55-": 1,
    },
    "asian/black": {
      "0-24": 1,
      "35-44": 1,
      "45-54": 2,
    },
  },
  M: {
    asian: {
      "35-44": 1,
      "55-": 1,
    },
    white: {
      "0-24": 1,
      "35-44": 1,
      "45-54": 2,
    },
  },
};

const isObject = obj => Object.prototype.toString.call(obj) === "[object Object]";

function nestKeys(obj, parent = "") {
  return Object.keys(obj).map(key => {
    const k = parent.length ? [parent, key].join(".") : key;

    if (!isObject(obj[key])) {
      return k;
    }

    return nestKeys(obj[key], k);
  }).flat();
}

function flatObj(obj) {
  const map = {};
  const keys = nestKeys(obj);

  keys.forEach(nestedKey => {
    const splited = nestedKey.split(".");

    const val = splited.reduce((acc, cur) => acc[cur], obj);

    splited.forEach(k => {
      map[k] = (map[k] || 0) + val;
    })
  });

  return map;
}


console.log(flatObj(data));

【讨论】:

  • 谢谢!您的代码运行完美且非常容易理解!
  • 嗨@albert-hsu 别担心,伙计。我看到你已经接受了我的回答,但你错过了投票。不过不急。玩得开心继续探索
  • 不幸的是,我刚开始参与 stackoverflow,所以没有足够的声誉来投票。 :( 我一到那里就会回来做的!
  • 别担心兄弟。
猜你喜欢
  • 2020-06-23
  • 2019-07-20
  • 2018-10-24
  • 1970-01-01
  • 2017-07-02
  • 2020-07-11
  • 2020-09-23
  • 2021-10-19
  • 2012-05-29
相关资源
最近更新 更多