热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使嵌套的JSON对象平坦/不平坦的最快方法-Fastestwaytoflatten/un-flattennestedJSONobjects

Ithrewsomecodetogethertoflattenandun-flattencomplexnestedJSONobjects.Itworks,butits

I threw some code together to flatten and un-flatten complex/nested JSON objects. It works, but it's a bit slow (triggers the 'long script' warning).

我将一些代码放在一起,以使复杂/嵌套的JSON对象变平或变平。它可以工作,但速度有点慢(触发“长脚本”警告)。

For the flattened names I want "." as the delimiter and [INDEX] for arrays.

作为数组的分隔符和[索引]。

Examples:

例子:

un-flattened | flattened
---------------------------
{foo:{bar:false}} => {"foo.bar":false}
{a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
[1,[2,[3,4],5],6] => {"[0]":1,"[1].[0]":2,"[1].[1].[0]":3,"[1].[1].[1]":4,"[1].[2]":5,"[2]":6}

I created a benchmark that ~simulates my use case http://jsfiddle.net/WSzec/

我创建了一个基准来模拟我的用例http://jsfiddle.net/WSzec/

  • Get a nested JSON object
  • 获取嵌套的JSON对象
  • Flatten it
  • 压平它
  • Look through it and possibly modify it while flattened
  • 检查它,并可能修改它时,扁平
  • Unflatten it back to it's original nested format to be shipped away
  • 把它拉回到原来的嵌套格式,然后再发送出去

I would like faster code: For clarification, code that completes the JSFiddle benchmark (http://jsfiddle.net/WSzec/) significantly faster (~20%+ would be nice) in IE 9+, FF 24+, and Chrome 29+.

我想要更快的代码:为了澄清这一点,在IE 9+、FF 24+和Chrome 29+中完成JSFiddle基准(http://jsfiddle.net/WSzec/)的代码要快得多(最好是20%+)。

Here's the relevant Javascript code: Current Fastest: http://jsfiddle.net/WSzec/6/

这里是相关的Javascript代码:当前最快的:http://jsfiddle.net/wszec/6/。

JSON.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var result = {}, cur, prop, idx, last, temp;
    for(var p in data) {
        cur = result, prop = "", last = 0;
        do {
            idx = p.indexOf(".", last);
            temp = p.substring(last, idx !== -1 ? idx : undefined);
            cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : {}));
            prop = temp;
            last = idx + 1;
        } while(idx >= 0);
        cur[prop] = data[p];
    }
    return result[""];
}
JSON.flatten = function(data) {
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
             for(var i=0, l=cur.length; i

EDIT 1 Modified the above to @Bergi 's implementation which is currently the fastest. As an aside, using ".indexOf" instead of "regex.exec" is around 20% faster in FF but 20% slower in Chrome; so I'll stick with the regex since it's simpler (here's my attempt at using indexOf to replace the regex http://jsfiddle.net/WSzec/2/).

编辑1将上面的修改为@Bergi的实现,这是目前最快的实现。顺便说一句,使用。indexOf”而不是“正则表达式。执行"在FF上快20%,在Chrome上慢20%;因此,我将继续使用regex,因为它更简单(我尝试使用indexOf替换regex http://jsfiddle.net/WSzec/2/)。

EDIT 2 Building on @Bergi 's idea I managed to created a faster non-regex version (3x faster in FF and ~10% faster in Chrome). http://jsfiddle.net/WSzec/6/ In the this (the current) implementation the rules for key names are simply, keys cannot start with an integer or contain a period.

基于@Bergi的想法,我创建了一个更快的非regex版本(FF快3倍,Chrome快10%)。http://jsfiddle.net/WSzec/6/在这个(当前)实现中,键名的规则很简单,键不能以整数开头或包含一个周期。

Example:

例子:

  • {"foo":{"bar":[0]}} => {"foo.bar.0":0}
  • { " foo ":{“酒吧”:[0]} } = > {“foo.bar.0”:0 }

EDIT 3 Adding @AaditMShah 's inline path parsing approach (rather than String.split) helped to improve the unflatten performance. I'm very happy with the overall performance improvement reached.

编辑3,添加@AaditMShah的内联路径解析方法(而不是String.split)有助于提高不平坦的性能。我对所取得的总体性能改进感到非常高兴。

The latest jsfiddle and jsperf:

最新的jsfiddle和jsperf:

http://jsfiddle.net/WSzec/14/

http://jsfiddle.net/WSzec/14/

http://jsperf.com/flatten-un-flatten/4

http://jsperf.com/flatten-un-flatten/4

11 个解决方案

#1


159  

Here's my much shorter implementation:

下面是我的简短实现:

Object.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
        resultholder = {};
    for (var p in data) {
        var cur = resultholder,
            prop = "",
            m;
        while (m = regex.exec(p)) {
            cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
            prop = m[2] || m[1];
        }
        cur[prop] = data[p];
    }
    return resultholder[""] || resultholder;
};

flatten hasn't changed much (and I'm not sure whether you really need those isEmpty cases):

flatten没怎么变(我不确定你是否真的需要那些空盒子):

Object.flatten = function(data) {
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
             for(var i=0, l=cur.length; i

Together, they run your benchmark in about the half of the time (Opera 12.16: ~900ms instead of ~ 1900ms, Chrome 29: ~800ms instead of ~1600ms).

它们一起在大约一半的时间内运行基准测试(Opera 12.16: ~900ms而不是~ 1900ms, Chrome 29: ~800ms而不是~1600ms)。

#2


16  

I wrote two functions to flatten and unflatten a JSON object.

我编写了两个函数来展开和展开JSON对象。


Flatten a JSON object:

平一个JSON对象:

var flatten = (function (isArray, wrapped) {
    return function (table) {
        return reduce("", {}, table);
    };

    function reduce(path, accumulator, table) {
        if (isArray(table)) {
            var length = table.length;

            if (length) {
                var index = 0;

                while (index 

Performance:

性能:

  1. It's faster than the current solution in Opera. The current solution is 26% slower in Opera.
  2. 它比Opera中当前的解决方案要快。目前的解决方案在Opera中慢了26%。
  3. It's faster than the current solution in Firefox. The current solution is 9% slower in Firefox.
  4. 它比Firefox中的当前解决方案要快。目前的解决方案在Firefox中慢了9%。
  5. It's faster than the current solution in Chrome. The current solution is 29% slower in Chrome.
  6. 它比Chrome当前的解决方案要快。目前的解决方案在Chrome上的速度要慢29%。

Unflatten a JSON object:

Unflatten JSON对象:

function unflatten(table) {
    var result = {};

    for (var path in table) {
        var cursor = result, length = path.length, property = "", index = 0;

        while (index 

Performance:

性能:

  1. It's faster than the current solution in Opera. The current solution is 5% slower in Opera.
  2. 它比Opera中当前的解决方案要快。目前的解决方案在Opera中要慢5%。
  3. It's slower than the current solution in Firefox. My solution is 26% slower in Firefox.
  4. 它比Firefox当前的解决方案要慢。我的解决方案在Firefox中慢了26%。
  5. It's slower than the current solution in Chrome. My solution is 6% slower in Chrome.
  6. 它比Chrome当前的解决方案要慢。我的解决方案在Chrome上慢了6%。

Flatten and unflatten a JSON object:

Flatten和unflatten一个JSON对象:

Overall my solution performs either equally well or even better than the current solution.

总体而言,我的解决方案的性能要么与当前解决方案一样好,要么甚至更好。

Performance:

性能:

  1. It's faster than the current solution in Opera. The current solution is 21% slower in Opera.
  2. 它比Opera中当前的解决方案要快。目前的解决方案在Opera中慢了21%。
  3. It's as fast as the current solution in Firefox.
  4. 它和Firefox中的当前解决方案一样快。
  5. It's faster than the current solution in Firefox. The current solution is 20% slower in Chrome.
  6. 它比Firefox中的当前解决方案要快。目前的解决方案是20%的慢铬。

Output format:

输出格式:

A flattened object uses the dot notation for object properties and the bracket notation for array indices:

扁平化对象使用对象属性的点表示法和数组索引的方括号表示法:

  1. {foo:{bar:false}} => {"foo.bar":false}
  2. { foo:{酒吧:假} } = > {“foo.bar”:假}
  3. {a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
  4. {:[{ b:[“c”,“d”]}]} = > {”[0]。b[0]”:“c”,“[0]。b[1]”:“d”}
  5. [1,[2,[3,4],5],6] => {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}
  6. [1,2、(3、4)、5)、6)= > {“[0]”:1、“[1][0]”:2,”[1][1][0]”:3”[1][1][1]”:4,“[1][2]”:5,“[2]”:6 }

In my opinion this format is better than only using the dot notation:

我认为这种格式比只使用点符号要好:

  1. {foo:{bar:false}} => {"foo.bar":false}
  2. { foo:{酒吧:假} } = > {“foo.bar”:假}
  3. {a:[{b:["c","d"]}]} => {"a.0.b.0":"c","a.0.b.1":"d"}
  4. {:[{ b:[“c”,“d”]}]} = > {“a.0.b.0”:“c”,“a.0.b.1”:“d”}
  5. [1,[2,[3,4],5],6] => {"0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6}
  6. [1,2、(3、4)、5)、6)= > {“0”:1、“1.0”:2,“1.1.0”:3、“1.1.1”:4,“1.2”:5,“2”:6 }

Advantages:

优点:

  1. Flattening an object is faster than the current solution.
  2. 压平一个物体比现在的解决方案快。
  3. Flattening and unflattening an object is as fast as or faster than the current solution.
  4. 扁平化和不扁平化一个物体的速度和当前的解决方案一样快或更快。
  5. Flattened objects use both the dot notation and the bracket notation for readability.
  6. 扁平对象使用点表示法和方括号表示法来提高可读性。

Disadvantages:

缺点:

  1. Unflattening an object is slower than the current solution in most (but not all) cases.
  2. 在大多数情况下(但不是所有情况下),不展平对象比当前的解决方案要慢。

The current JSFiddle demo gave the following values as output:

当前的JSFiddle demo给出的输出值为:

Nested : 132175 : 63
Flattened : 132175 : 564
Nested : 132175 : 54
Flattened : 132175 : 508

My updated JSFiddle demo gave the following values as output:

我更新的JSFiddle demo给出如下值作为输出:

Nested : 132175 : 59
Flattened : 132175 : 514
Nested : 132175 : 60
Flattened : 132175 : 451

I'm not really sure what that means, so I'll stick with the jsPerf results. After all jsPerf is a performance benchmarking utility. JSFiddle is not.

我不太清楚这是什么意思,所以我将继续使用jsPerf结果。毕竟jsPerf是一个性能基准工具。JSFiddle不是。

#3


10  

Based on @Bergi's code I made a simple webpage to flatten // unflatten.

基于@Bergi的代码,我做了一个简单的网页来平平//平。

http://fiddle.jshell.net/blowsie/S2hsS/show/light/

http://fiddle.jshell.net/blowsie/S2hsS/show/light/

enter image description here

JSON.flatten = function (data) {
    var result = {};

    function recurse(cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
            for (var i = 0, l = cur.length; i 
body {
    padding:20px;
}

JSON Flattener



#4


6  

Here's another approach that runs slower (about 1000ms) than the above answer, but has an interesting idea :-)

这是另一种比上述答案慢的方法(大约1000毫秒),但有一个有趣的想法:-)

Instead of iterating through each property chain, it just picks the last property and uses a look-up-table for the rest to store the intermediate results. This look-up-table will be iterated until there are no property chains left and all values reside on uncocatenated properties.

它没有遍历每个属性链,而是选择最后一个属性,并使用查找表来存储中间结果。这个查找表将被迭代,直到没有剩余的属性链,所有的值都位于未连接的属性上。

JSON.unflatten = function(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)$|\[(\d+)\]$/,
        props = Object.keys(data),
        result, p;
    while(p = props.shift()) {
        var m = regex.exec(p),
            target;
        if (m.index) {
            var rest = p.slice(0, m.index);
            if (!(rest in data)) {
                data[rest] = m[2] ? [] : {};
                props.push(rest);
            }
            target = data[rest];
        } else {
            target = result || (result = (m[2] ? [] : {}));
        }
        target[m[2] || m[1]] = data[p];
    }
    return result;
};

It currently uses the data input parameter for the table, and puts lots of properties on it - a non-destructive version should be possible as well. Maybe a clever lastIndexOf usage performs better than the regex (depends on the regex engine).

它目前使用表的数据输入参数,并在表上放置了许多属性——一个非破坏性的版本也应该是可能的。也许一个聪明的lastIndexOf usage比regex性能更好(取决于regex引擎)。

See it in action here.

看这里的行动。

#5


4  

3 ½ Years later...

3½年后…

For my own project I wanted to flatten JSON objects in mongoDB dot notation and came up with a simple solution:

对于我自己的项目,我想用mongoDB点表示法简化JSON对象,并提出了一个简单的解决方案:

/**
 * Recursively flattens a JSON object using dot notation.
 *
 * NOTE: input must be an object as described by JSON spec. Arbitrary
 * JS objects (e.g. {a: () => 42}) may result in unexpected output.
 * MOREOVER, it removes keys with empty objects/arrays as value (see
 * examples bellow).
 *
 * @example
 * // returns {a:1, 'b.0.c': 2, 'b.0.d.e': 3, 'b.1': 4}
 * flatten({a: 1, b: [{c: 2, d: {e: 3}}, 4]})
 * // returns {a:1, 'b.0.c': 2, 'b.0.d.e.0': true, 'b.0.d.e.1': false, 'b.0.d.e.2.f': 1}
 * flatten({a: 1, b: [{c: 2, d: {e: [true, false, {f: 1}]}}]})
 * // return {a: 1}
 * flatten({a: 1, b: [], c: {}})
 *
 * @param obj item to be flattened
 * @param {Array.string} [prefix=[]] chain of prefix joined with a dot and prepended to key
 * @param {Object} [current={}] result of flatten during the recursion
 *
 * @see https://docs.mongodb.com/manual/core/document/#dot-notation
 */
function flatten (obj, prefix, current) {
  prefix = prefix || []
  current = current || {}

  // Remember kids, null is also an object!
  if (typeof (obj) === 'object' && obj !== null) {
    Object.keys(obj).forEach(key => {
      this.flatten(obj[key], prefix.concat(key), current)
    })
  } else {
    current[prefix.join('.')] = obj
  }

  return current
}

Features and/or caveats

特性和/或警告

  • It only accepts JSON objects. So if you pass something like {a: () => {}} you might not get what you wanted!
  • 它只接受JSON对象。因此,如果您传递类似{a:() =>{}的内容,您可能不会得到您想要的!
  • It removes empty arrays and objects. So this {a: {}, b: []} is flattened to {}.
  • 它删除空数组和对象。所以这个{},b:[]}被压平为{}。

#6


2  

This code recursively flattens out JSON objects.

这段代码递归地将JSON对象变平。

I included my timing mechanism in the code and it gives me 1ms but I'm not sure if that's the most accurate one.

我在代码中加入了计时机制,它给了我1ms,但我不确定这是否是最准确的。

            var new_json = [{
              "name": "fatima",
              "age": 25,
              "neighbour": {
                "name": "taqi",
                "location": "end of the street",
                "property": {
                  "built in": 1990,
                  "owned": false,
                  "years on market": [1990, 1998, 2002, 2013],
                  "year short listed": [], //means never
                }
              },
              "town": "Mountain View",
              "state": "CA"
            },
            {
              "name": "qianru",
              "age": 20,
              "neighbour": {
                "name": "joe",
                "location": "opposite to the park",
                "property": {
                  "built in": 2011,
                  "owned": true,
                  "years on market": [1996, 2011],
                  "year short listed": [], //means never
                }
              },
              "town": "Pittsburgh",
              "state": "PA"
            }]

            function flatten(json, flattened, str_key) {
                for (var key in json) {
                  if (json.hasOwnProperty(key)) {
                    if (json[key] instanceof Object && json[key] != "") {
                      flatten(json[key], flattened, str_key + "." + key);
                    } else {
                      flattened[str_key + "." + key] = json[key];
                    }
                  }
                }
            }

        var flattened = {};
        console.time('flatten'); 
        flatten(new_json, flattened, "");
        console.timeEnd('flatten');

        for (var key in flattened){
          console.log(key + ": " + flattened[key]);
        }

Output:

输出:

flatten: 1ms
.0.name: fatima
.0.age: 25
.0.neighbour.name: taqi
.0.neighbour.location: end of the street
.0.neighbour.property.built in: 1990
.0.neighbour.property.owned: false
.0.neighbour.property.years on market.0: 1990
.0.neighbour.property.years on market.1: 1998
.0.neighbour.property.years on market.2: 2002
.0.neighbour.property.years on market.3: 2013
.0.neighbour.property.year short listed: 
.0.town: Mountain View
.0.state: CA
.1.name: qianru
.1.age: 20
.1.neighbour.name: joe
.1.neighbour.location: opposite to the park
.1.neighbour.property.built in: 2011
.1.neighbour.property.owned: true
.1.neighbour.property.years on market.0: 1996
.1.neighbour.property.years on market.1: 2011
.1.neighbour.property.year short listed: 
.1.town: Pittsburgh
.1.state: PA

#7


2  

You can use https://github.com/hughsk/flat

您可以使用https://github.com/hughsk/flat

Take a nested Javascript object and flatten it, or unflatten an object with delimited keys.

获取嵌套的Javascript对象并将其展开,或者使用分隔键将对象展开。

Example from the doc
var flatten = require('flat')

flatten({
    key1: {
        keyA: 'valueI'
    },
    key2: {
        keyB: 'valueII'
    },
    key3: { a: { b: { c: 2 } } }
})

// {
//   'key1.keyA': 'valueI',
//   'key2.keyB': 'valueII',
//   'key3.a.b.c': 2
// }


var unflatten = require('flat').unflatten

unflatten({
    'three.levels.deep': 42,
    'three.levels': {
        nested: true
    }
})

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }

#8


2  

ES6 version:

ES6版本:

const flatten = (obj, path = '') => {        
    if (!(obj instanceof Object)) return {[path.replace(/\.$/g, '')]:obj};

    return Object.keys(obj).reduce((output, key) => {
        return obj instanceof Array ? 
             {...output, ...flatten(obj[key], path +  '[' + key + '].')}:
             {...output, ...flatten(obj[key], path + key + '.')};
    }, {});
}

Example:

例子:

console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));

#9


1  

I added +/- 10-15% efficiency to the selected answer by minor code refactoring and moving the recursive function outside of the function namespace.

我通过少量的代码重构和将递归函数移到函数名称空间之外,为所选答案增加了+/- 10-15%的效率。

See my question: Are namespaced functions reevaluated on every call? for why this slows nested functions down.

请参见我的问题:命名空间函数是否在每次调用中都重新计算?这就是为什么这会降低嵌套函数的速度。

function _flatten (target, obj, path) {
  var i, empty;
  if (obj.cOnstructor=== Object) {
    empty = true;
    for (i in obj) {
      empty = false;
      _flatten(target, obj[i], path ? path + '.' + i : i);
    }
    if (empty && path) {
      target[path] = {};
    }
  } 
  else if (obj.cOnstructor=== Array) {
    i = obj.length;
    if (i > 0) {
      while (i--) {
        _flatten(target, obj[i], path + '[' + i + ']');
      }
    } else {
      target[path] = [];
    }
  }
  else {
    target[path] = obj;
  }
}

function flatten (data) {
  var result = {};
  _flatten(result, data, null);
  return result;
}

See benchmark.

看到基准。

#10


1  

Here's mine. It runs in <2ms in Google Apps Script on a sizable object. It uses dashes instead of dots for separators, and it doesn't handle arrays specially like in the asker's question, but this is what I wanted for my use.

这是我的。它在一个相当大的对象上以<2ms的速度运行在谷歌应用程序脚本中。它使用破折号而不是点作为分隔符,它不像asker的问题那样处理数组,但这是我想要的。

function flatten (obj) {
  var newObj = {};
  for (var key in obj) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      var temp = flatten(obj[key])
      for (var key2 in temp) {
        newObj[key+"-"+key2] = temp[key2];
      }
    } else {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Example:

例子:

var test = {
  a: 1,
  b: 2,
  c: {
    c1: 3.1,
    c2: 3.2
  },
  d: 4,
  e: {
    e1: 5.1,
    e2: 5.2,
    e3: {
      e3a: 5.31,
      e3b: 5.32
    },
    e4: 5.4
  },
  f: 6
}

Logger.log("start");
Logger.log(JSON.stringify(flatten(test),null,2));
Logger.log("done");

Example output:

示例输出:

[17-02-08 13:21:05:245 CST] start
[17-02-08 13:21:05:246 CST] {
  "a": 1,
  "b": 2,
  "c-c1": 3.1,
  "c-c2": 3.2,
  "d": 4,
  "e-e1": 5.1,
  "e-e2": 5.2,
  "e-e3-e3a": 5.31,
  "e-e3-e3b": 5.32,
  "e-e4": 5.4,
  "f": 6
}
[17-02-08 13:21:05:247 CST] done

#11


0  

I'd like to add a new version of flatten case (this is what i needed :)) which, according to my probes with the above jsFiddler, is slightly faster then the currently selected one. Moreover, me personally see this snippet a bit more readable, which is of course important for multi-developer projects.

我想添加一个新版本的flatten case(这是我需要的:)),根据我对上面的jsFiddler的探针,它比当前选择的稍微快一些。此外,我个人认为这段代码可读性更好,这对于多开发项目来说是很重要的。

function flattenObject(graph) {
    let result = {},
        item,
        key;

    function recurr(graph, path) {
        if (Array.isArray(graph)) {
            graph.forEach(function (itm, idx) {
                key = path + '[' + idx + ']';
                if (itm && typeof itm === 'object') {
                    recurr(itm, key);
                } else {
                    result[key] = itm;
                }
            });
        } else {
            Reflect.ownKeys(graph).forEach(function (p) {
                key = path + '.' + p;
                item = graph[p];
                if (item && typeof item === 'object') {
                    recurr(item, key);
                } else {
                    result[key] = item;
                }
            });
        }
    }
    recurr(graph, '');

    return result;
}

推荐阅读
author-avatar
z235235_795
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有