javascript - Fastest way to flatten / un-flatten nested JSON objects -
i threw code flatten , un-flatten complex/nested json objects. works, it's bit slow (triggers 'long script' warning).
for flattened names want "." delimiter , [index] 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 benchmark ~simulates use case http://jsfiddle.net/wszec/
- get nested json object
- flatten it
- look through , possibly modify while flattened
- unflatten it's original nested format shipped away
i faster code: clarification, code completes jsfiddle benchmark (http://jsfiddle.net/wszec/) faster (~20%+ nice) in ie 9+, ff 24+, , chrome 29+.
here's relevant javascript code: current fastest: 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; { 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<l; i++) recurse(cur[i], prop ? prop+"."+i : ""+i); if (l == 0) result[prop] = []; } else { var isempty = true; (var p in cur) { isempty = false; recurse(cur[p], prop ? prop+"."+p : p); } if (isempty) result[prop] = {}; } } recurse(data, ""); return result; }
edit 1 modified above @bergi 's implementation fastest. aside, using ".indexof" instead of "regex.exec" around 20% faster in ff 20% slower in chrome; i'll stick regex since it's simpler (here's attempt @ using indexof replace regex http://jsfiddle.net/wszec/2/).
edit 2 building on @bergi 's idea managed created faster non-regex version (3x faster in ff , ~10% faster in chrome). http://jsfiddle.net/wszec/6/ in (the current) implementation rules key names simply, keys cannot start integer or contain period.
example:
- {"foo":{"bar":[0]}} => {"foo.bar.0":0}
edit 3 adding @aaditmshah 's inline path parsing approach (rather string.split) helped improve unflatten performance. i'm happy overall performance improvement reached.
the latest jsfiddle , jsperf:
here's shorter implementation:
object.unflatten = function(data) { "use strict"; if (object(data) !== data || array.isarray(data)) return data; var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g, resultholder = {}; (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 (and i'm not sure whether need isempty
cases):
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<l; i++) recurse(cur[i], prop + "[" + + "]"); if (l == 0) result[prop] = []; } else { var isempty = true; (var p in cur) { isempty = false; recurse(cur[p], prop ? prop+"."+p : p); } if (isempty && prop) result[prop] = {}; } } recurse(data, ""); return result; }
together, run benchmark in half of time (opera 12.16: ~900ms instead of ~ 1900ms, chrome 29: ~800ms instead of ~1600ms).
Comments
Post a Comment