在JavaScript中压缩对象层次结构

10

是否有一种通用的方法将嵌套对象“压缩”到一个级别:

var myObj = {
    a: "hello",
    b: {
        c: "world"
    }
}

compress(myObj) == {
    a: "hello",
    b_c: "world"
}

我猜这里可能涉及到一些递归,但我想我不需要在这里重新发明轮子... !?


你为什么需要这个?你想在构建时处理你的JavaScript,然后运行时JS性能会得到改善吗?但是如果你需要操作复杂对象模型(例如子对象(嵌套对象)必须作为某个函数的参数传递),那么在压缩后如何访问你的JS中的嵌套对象呢? - Svitlana Maksymchuk
1
我需要这个用于数据映射,其中处理过程不处理嵌套对象。 - AnC
当我在使用嵌套对象与Redis时,我有一个类似于AnC的需求,因为它只支持平面哈希。最终我使用了Matthew Crumley的解决方案CoffeeScript版本 - Michael Hart
3个回答

21
function flatten(obj, includePrototype, into, prefix) {
    into = into || {};
    prefix = prefix || "";

    for (var k in obj) {
        if (includePrototype || obj.hasOwnProperty(k)) {
            var prop = obj[k];
            if (prop && typeof prop === "object" &&
                !(prop instanceof Date || prop instanceof RegExp)) {
                flatten(prop, includePrototype, into, prefix + k + "_");
            }
            else {
                into[prefix + k] = prop;
            }
        }
    }

    return into;
}

通过将第二个参数传递为true,您可以包含继承的成员。

但是需要注意以下几点:

  • 递归对象不起作用。例如:

    var o = { a: "foo" };
    o.b = o;
    flatten(o);
    

    该函数会递归执行,直到抛出异常。

  • 和ruquay的答案一样,它像访问普通对象属性一样提取数组元素。如果您想保持数组完整性,请将“|| prop instanceof Array”添加到异常中。

  • 如果您在不同窗口或框架中调用此函数的对象,则日期和正则表达式将不包括在内,因为instanceof将无法正常工作。您可以通过像这样替换为默认的toString方法来解决这个问题:

    Object.prototype.toString.call(prop) === "[object Date]"
    Object.prototype.toString.call(prop) === "[object RegExp]"
    Object.prototype.toString.call(prop) === "[object Array]"
    

哇,这似乎非常好用!非常感谢你提供的详细文档 - 我真的很感激! - AnC

4

以下是一份快速的代码,但要注意,它不能用于数组和null值(因为它们的typeof返回值为“object”)。

var flatten = function(obj, prefix) {
  if(typeof prefix === "undefined") {
    prefix = "";
  }
  var copy = {};
  for (var p in obj) {
    if(obj.hasOwnProperty(p)) {
      if(typeof obj[p] === "object") {
        var tmp = flatten(obj[p], p + "_");
        for(var q in tmp) {
          if(tmp.hasOwnProperty(q)) {
            copy[prefix + q] = tmp[q];
          }
        }
      }
      else {
        copy[prefix + p] = obj[p];
      }
    }
  }
  return copy;
}

var myObj = {
  a: "level 1",
  b: {
    a: "level 2",
    b: {
      a: "level 3",
      b: "level 3"
    }
  }
}

var flattened = flatten(myObj);

谢谢你。它还没有完全工作(请参见下面的测试对象);我会深入研究并在这里报告任何进展。(如前所述,我预计这是一个已解决的问题 - 即在某些JavaScript食谱中会有一个现成的函数...)var myObj = { a1: "level 1", a2: { b1: 99, b2: { c1: new Date(), c2: "level 3" }, b3: "asd" }, a3: /foo/ }; - AnC

2
这是一个快速的CoffeeScript版本,基于Matthew Crumley的答案(我没有使用includePrototype,因为我不需要它):
flatten = (obj, into = {}, prefix = '', sep = '_') ->
  for own key, prop of obj
    if typeof prop is 'object' and prop not instanceof Date and prop not instanceof RegExp
      flatten prop, into, prefix + key + sep, sep
    else
      into[prefix + key] = prop
  into

一种基本的未压平版本,无疑会在重复分隔符和其他类似的棘手情况下失败:
unflatten = (obj, into = {}, sep = '_') ->
  for own key, prop of obj
    subKeys = key.split sep
    sub = into
    sub = (sub[subKey] or= {}) for subKey in subKeys[...-1]
    sub[subKeys.pop()] = prop
  into

就我所知,我使用这些函数将对象图推入Redis哈希中,该哈希仅支持单层键/值对。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接