Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 展平/取消展平嵌套JSON对象的最快方法_Javascript_Algorithm - Fatal编程技术网

Javascript 展平/取消展平嵌套JSON对象的最快方法

Javascript 展平/取消展平嵌套JSON对象的最快方法,javascript,algorithm,Javascript,Algorithm,我将一些代码放在一起以展平和取消展平复杂/嵌套的JSON对象。它可以工作,但有点慢(触发“长脚本”警告) 对于展开的名称,我希望“.”作为分隔符,而[INDEX]作为数组 示例: 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

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

对于展开的名称,我希望“.”作为分隔符,而[INDEX]作为数组

示例:

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}
我创建了一个模拟我的用例的基准

  • 获取嵌套的JSON对象
  • 压扁它
  • 查看并可能在展平时对其进行修改
  • 将其解压缩回原始嵌套格式,以便运走
我想要更快的代码:为了澄清,在IE 9+、FF 24+和Chrome 29+中完成JSFIDLE基准()的代码要快得多(~20%+就好了)。

以下是相关的JavaScript代码:当前最快:

JSON.unflatten=函数(数据){
“严格使用”;
if(对象(数据)!==数据| |数组.isArray(数据))
返回数据;
var result={},cur,prop,idx,last,temp;
for(数据中的var p){
cur=结果,prop=“”,last=0;
做{
idx=p.indexOf(“.”,最后一个);
temp=p.substring(最后一个,idx!=-1?idx:未定义);
cur=cur[prop]| |(cur[prop]=(!isNaN(parseInt(temp))?[]:{});
prop=温度;
last=idx+1;
}而(idx>=0);
cur[prop]=数据[p];
}
返回结果[“”];
}
JSON.flant=函数(数据){
var result={};
函数递归(cur,prop){
if(对象(cur)!==cur){
结果[prop]=cur;
}else if(Array.isArray(cur)){

对于(var i=0,l=cur.length;i这是我更短的实现:

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;
};
flatte
变化不大(我不确定您是否真的需要那些
isEmpty
案例):

Object.flatte=函数(数据){
var result={};
函数递归(cur,prop){
if(对象(cur)!==cur){
结果[prop]=cur;
}else if(Array.isArray(cur)){

对于(var i=0,l=cur.length;i这里有另一种方法比上面的答案运行得慢(大约1000毫秒),但有一个有趣的想法:-)

它不会遍历每个属性链,而是只选取最后一个属性,并使用一个查找表存储其余属性的中间结果。该查找表将被迭代,直到没有剩余的属性链,并且所有值都驻留在未链接的属性上

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;
};
它目前为表使用了
数据
输入参数,并在其上添加了许多属性-也应该可以使用非破坏性版本。可能聪明的
lastIndexOf
用法比正则表达式性能更好(取决于正则表达式引擎)


.

我为JSON对象编写了两个函数
flatten
unflatten


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 < length) {
                    var property = path + "[" + index + "]", item = table[index++];
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            } else accumulator[path] = table;
        } else {
            var empty = true;

            if (path) {
                for (var property in table) {
                    var item = table[property], property = path + "." + property, empty = false;
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            } else {
                for (var property in table) {
                    var item = table[property], empty = false;
                    if (wrapped(item) !== item) accumulator[property] = item;
                    else reduce(property, accumulator, item);
                }
            }

            if (empty) accumulator[path] = table;
        }

        return accumulator;
    }
}(Array.isArray, Object));
function unflatten(table) {
    var result = {};

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

        while (index < length) {
            var char = path.charAt(index);

            if (char === "[") {
                var start = index + 1,
                    end = path.indexOf("]", start),
                    cursor = cursor[property] = cursor[property] || [],
                    property = path.slice(start, end),
                    index = end + 1;
            } else {
                var cursor = cursor[property] = cursor[property] || {},
                    start = char === "." ? index + 1 : index,
                    bracket = path.indexOf("[", start),
                    dot = path.indexOf(".", start);

                if (bracket < 0 && dot < 0) var end = index = length;
                else if (bracket < 0) var end = index = dot;
                else if (dot < 0) var end = index = bracket;
                else var end = index = bracket < dot ? bracket : dot;

                var property = path.slice(start, end);
            }
        }

        cursor[property] = table[path];
    }

    return result[""];
}
My Update将以下值作为输出:

Nested : 132175 : 63
Flattened : 132175 : 564
Nested : 132175 : 54
Flattened : 132175 : 508
Nested : 132175 : 59
Flattened : 132175 : 514
Nested : 132175 : 60
Flattened : 132175 : 451

我不确定这意味着什么,所以我将坚持使用jsPerf结果。毕竟,jsPerf是一个性能基准测试实用程序。JSFIDLE不是。

这段代码递归地使JSON对象平坦化

我在代码中加入了计时机制,它给了我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]);
        }
输出:

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

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

请参阅我的问题:了解为什么这会减慢嵌套函数的速度

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;
}

请参阅。

我想添加一个新版本的Flatte 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;
}
你可以用

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

文档中的示例 这是我的。它在三年半后运行…

对于我自己的项目,我想在中展平JSON对象,并提出了一个简单的解决方案:

/**
*使用点表示法递归展平JSON对象。
*
*注意:输入必须是JSON规范描述的对象。任意
*JS对象(例如,{a:()=>42})可能会导致意外的输出。
*此外,它还会删除以空对象/数组作为值的键(请参见
*示例如下)。
*
*@example
*//返回{a:1,'b.0.c':2,'b.0.d.e':3,'b.1':4}
*展平({a:1,b:[{c:2,d:{e:3},4]})
*//返回{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}
*展平({a:1,b:[{c:2,d:{e:[true,false,{f:1}]}})
*//返回{a:1}
*展平({a:1,b:[],c:{})
*
*@param obj要展平的项目
*@param{Array.string}[prefix=[]前缀链,以点连接,并在键的前面
*@param{Object}[current={}]递归期间展平的结果
*
*@见https://docs.mongodb.com/manual/core/document/#dot-符号
*/
函数展平(对象、前缀、当前){
前缀=前缀| |[]
当前=当前| |{}
//记住孩子们,null也是一个对象!
if(typeof(obj)=='object'&&obj!==null){
Object.keys(obj.forEach)(key=>{
this.flant(对象[key],前缀.concat(key),当前)
})
}否则{
当前[prefix.join('.')]=obj
}
回流
}
功能和/或注意事项

  • 它只接受JSON对象,所以如果你传递像
    {a:()=>{}
    这样的东西,你可能得不到你想要的
  • 它删除空数组和对象。因此,这个
    {a:{},b:[]}
    被展平为
    {}
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 + '.')};
    }, {});
}
例如:

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");
console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));
'{"name": "John","Address": {"house": "1234", "Street": "Boogie Ave"}, "pets": [{"Type": "Dog", "Age": 4, "Toys": ["rubberBall", "rope"]},{"Type": "Cat", "Age": 7, "Toys": ["catNip"]}]}' | ConvertFrom-Json | ConvertTo-JhcUtilJsonTable
使用此库:

npm install flat
用法(从):

展平:

    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
//         }
//     }
// }

下面是我编写的一些代码,用于展平我正在处理的对象。它创建了一个新类,该类接受每个嵌套字段并将其带到第一层。您可以
console.log(flatten({a:[{b:["c","d"]}]}));
console.log(flatten([1,[2,[3,4],5],6]));
npm install flat
    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
//         }
//     }
// }
class JSONFlattener {
    ojson = {}
    flattenedjson = {}

    constructor(original_json) {
        this.ojson = original_json
        this.flattenedjson = {}
        this.flatten()
    }

    flatten() {
        Object.keys(this.ojson).forEach(function(key){
            if (this.ojson[key] == null) {

            } else if (this.ojson[key].constructor == ({}).constructor) {
                this.combine(new JSONFlattener(this.ojson[key]).returnJSON())
            } else {
                this.flattenedjson[key] = this.ojson[key]
            }
        }, this)        
    }

    combine(new_json) {
        //assumes new_json is a flat array
        Object.keys(new_json).forEach(function(key){
            if (!this.flattenedjson.hasOwnProperty(key)) {
                this.flattenedjson[key] = new_json[key]
            } else {
                console.log(key+" is a duplicate key")
            }
        }, this)
    }

    returnJSON() {
        return this.flattenedjson
    }
}

console.log(new JSONFlattener(dad_dictionary).returnJSON())
nested_json = {
    "a": {
        "b": {
            "c": {
                "d": {
                    "a": 0
                }
            }
        }
    },
    "z": {
        "b":1
    },
    "d": {
        "c": {
            "c": 2
        }
    }
}
{ a: 0, b: 1, c: 2 }

#---helper function for ConvertTo-JhcUtilJsonTable
#
function getNodes {
    param (
        [Parameter(Mandatory)]
        [System.Object]
        $job,
        [Parameter(Mandatory)]
        [System.String]
        $path
    )

    $t = $job.GetType()
    $ct = 0
    $h = @{}

    if ($t.Name -eq 'PSCustomObject') {
        foreach ($m in Get-Member -InputObject $job -MemberType NoteProperty) {
            getNodes -job $job.($m.Name) -path ($path + '.' + $m.Name)
        }
        
    }
    elseif ($t.Name -eq 'Object[]') {
        foreach ($o in $job) {
            getNodes -job $o -path ($path + "[$ct]")
            $ct++
        }
    }
    else {
        $h[$path] = $job
        $h
    }
}


#---flattens a JSON document object into a key value table where keys are proper JSON paths corresponding to their value
#
function ConvertTo-JhcUtilJsonTable {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [System.Object[]]
        $jsonObj
    )

    begin {
        $rootNode = 'root'    
    }
    
    process {
        foreach ($o in $jsonObj) {
            $table = getNodes -job $o -path $rootNode

            # $h = @{}
            $a = @()
            $pat = '^' + $rootNode
            
            foreach ($i in $table) {
                foreach ($k in $i.keys) {
                    # $h[$k -replace $pat, ''] = $i[$k]
                    $a += New-Object -TypeName psobject -Property @{'Key' = $($k -replace $pat, ''); 'Value' = $i[$k]}
                    # $h[$k -replace $pat, ''] = $i[$k]
                }
            }
            # $h
            $a
        }
    }

    end{}
}
'{"name": "John","Address": {"house": "1234", "Street": "Boogie Ave"}, "pets": [{"Type": "Dog", "Age": 4, "Toys": ["rubberBall", "rope"]},{"Type": "Cat", "Age": 7, "Toys": ["catNip"]}]}' | ConvertFrom-Json | ConvertTo-JhcUtilJsonTable
Key              Value
---              -----
.Address.house   1234
.Address.Street  Boogie Ave
.name            John
.pets[0].Age     4
.pets[0].Toys[0] rubberBall
.pets[0].Toys[1] rope
.pets[0].Type    Dog
.pets[1].Age     7
.pets[1].Toys[0] catNip
.pets[1].Type    Cat
let flatFoo = await require('jpflat').flatten(foo)