Javascript 展平/取消展平嵌套JSON对象的最快方法
我将一些代码放在一起以展平和取消展平复杂/嵌套的JSON对象。它可以工作,但有点慢(触发“长脚本”警告) 对于展开的名称,我希望“.”作为分隔符,而[INDEX]作为数组 示例: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
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对象
- 压扁它
- 查看并可能在展平时对其进行修改
- 将其解压缩回原始嵌套格式,以便运走
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)