在 JavaScript 中压缩对象层次结构

Posted

技术标签:

【中文标题】在 JavaScript 中压缩对象层次结构【英文标题】:compressing object hierarchies in JavaScript 【发布时间】:2010-11-01 02:33:24 【问题描述】:

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

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


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

我想这会涉及到一些递归,但我认为我不需要在这里重新发明***......!?

【问题讨论】:

你为什么需要那个?您想在构建期间处理您的 javascript,然后提高运行时 js 性能吗?但是,如果您需要使用复杂的对象模型进行操作(例如子对象(嵌套对象)必须作为参数传递给某个函数),您将如何从您的 js 访问嵌套对象(压缩后)? 我需要这个来处理不处理嵌套对象的数据映射。 在使用 Redis 的嵌套对象时,我对 AnC 有类似的需求,因为它只支持平面哈希。我最终使用了CoffeeScript version 的Matthew Crumley's solution。 【参考方案1】:
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]"

【讨论】:

哇,这似乎很好用!非常感谢,也感谢详细的文档 - 我真的很感激!【参考方案2】:

这是一个快速的方法,但请注意,b/c 它不会 使用数组和空值(b/c 它们的 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/ ;【参考方案3】:

这是基于Matthew Crumley's answer 的快速CoffeeScript 版本(我没有使用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

FWIW,我使用这些函数将对象图推送到 Redis hashes,它只支持单一深度的键/值对。

【讨论】:

以上是关于在 JavaScript 中压缩对象层次结构的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ipad 中压缩文件夹

如何在 TYPO3 中压缩我的 js/css 文件

如何在 Django 中压缩 JSON 请求?

如何在 Android 中压缩字符串

在数据框中压缩相似的实例,同时添加每个实例的特征

在python中压缩文件