Last active
December 6, 2019 09:40
-
-
Save bsingr/b12f0e04284bc3ef0a1142e4e6944522 to your computer and use it in GitHub Desktop.
flatten deep hash/array/scalar data structure into flat list with jsonpath keys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FlatTransformer | |
# @param [Hash, Array, String, Fixnum] | |
# @return [Enumerator, #to_a] enumerator, call #to_a to get it as array | |
def self.deep_flatten_jsonpath(data, max_depth=nil) | |
Enumerator.new do |enum| | |
depth = 0 | |
queue = [{ path: [], value: data }] | |
while queue.length > 0 | |
current = queue.shift | |
is_allowed_to_go_deeper = (!max_depth || depth < max_depth) | |
if is_allowed_to_go_deeper && current[:value].is_a?(Array) | |
current[:value].each_with_index do |value, index| | |
queue.push({ | |
path: current[:path] + [index], | |
value: value | |
}) | |
end | |
elsif is_allowed_to_go_deeper && current[:value].is_a?(Hash) | |
current[:value].each do |key, value| | |
queue.push({ | |
path: current[:path] + [key], | |
value: value | |
}) | |
end | |
else | |
enum.yield({ | |
key: current[:path].empty? ? 'root' : current[:path].join('.'), | |
value: current[:value].to_s | |
}) | |
end | |
depth = depth += 1 | |
end | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'minitest/autorun' | |
require './FlatTransformer' | |
class Object | |
def sortme | |
self.to_a.sort_by{ |obj| obj[:key] } | |
end | |
end | |
describe "FlatTransformer.deep_flatten_jsonpath" do | |
it "must return scalar as array" do | |
FlatTransformer.deep_flatten_jsonpath('GET http://example.com').sortme.must_equal([ | |
{key: 'root', value: 'GET http://example.com'} | |
].sortme) | |
end | |
it "must return array as array" do | |
FlatTransformer.deep_flatten_jsonpath([ | |
'GET http://example.com', | |
'Ok' | |
]) | |
.sortme.must_equal([ | |
{key: '0', value: 'GET http://example.com'}, | |
{key: '1', value: 'Ok'} | |
].sortme) | |
end | |
it "must return hash as array" do | |
FlatTransformer.deep_flatten_jsonpath({ | |
request: 'GET http://example.com', | |
response: 'Ok' | |
}) | |
.sortme.must_equal([ | |
{key: 'request', value: 'GET http://example.com'}, | |
{key: 'response', value: 'Ok'} | |
].sortme) | |
end | |
it "must flatten nested hash" do | |
FlatTransformer.deep_flatten_jsonpath({ | |
request: { | |
host: 'example.com', | |
cookie: 'TOKEN=secret' | |
}, | |
response: 'Ok' | |
}) | |
.sortme.must_equal([ | |
{key: 'request.host', value: 'example.com'}, | |
{key: 'request.cookie', value: 'TOKEN=secret'}, | |
{key: 'response', value: 'Ok'} | |
].sortme) | |
end | |
it "must flatten hash of arrays" do | |
FlatTransformer.deep_flatten_jsonpath({ | |
requests: [ | |
'GET http://example.com/1', | |
'GET http://example.com/2', | |
'GET http://example.com/3' | |
] | |
}) | |
.sortme.must_equal([ | |
{key: 'requests.0', value: 'GET http://example.com/1'}, | |
{key: 'requests.1', value: 'GET http://example.com/2'}, | |
{key: 'requests.2', value: 'GET http://example.com/3'} | |
].sortme) | |
end | |
it "must flatten deep array structure" do | |
FlatTransformer.deep_flatten_jsonpath({ | |
grouped_requests: [ | |
[ | |
'GET http://example.com/pages', | |
[ | |
'GET http://example.com/pages/1', | |
'GET http://example.com/pages/2', | |
'GET http://example.com/pages/3' | |
] | |
], | |
[ | |
'GET http://www.example.com/1', | |
'GET http://www.example.com/2', | |
'GET http://www.example.com/3' | |
] | |
] | |
}) | |
.sortme.must_equal([ | |
{key: 'grouped_requests.0.0', value: 'GET http://example.com/pages'}, | |
{key: 'grouped_requests.0.1.0', value: 'GET http://example.com/pages/1'}, | |
{key: 'grouped_requests.0.1.1', value: 'GET http://example.com/pages/2'}, | |
{key: 'grouped_requests.0.1.2', value: 'GET http://example.com/pages/3'}, | |
{key: 'grouped_requests.1.0', value: 'GET http://www.example.com/1'}, | |
{key: 'grouped_requests.1.1', value: 'GET http://www.example.com/2'}, | |
{key: 'grouped_requests.1.2', value: 'GET http://www.example.com/3'} | |
].sortme) | |
end | |
it "must flatten deep hash structure" do | |
FlatTransformer.deep_flatten_jsonpath({ | |
grouped_requests: { | |
'example.com': { | |
method: 'get', | |
headers: { | |
cookie: 'TOKEN=secret' | |
} | |
}, | |
'www.example.com': { | |
method: 'get', | |
headers: { | |
cookie: 'TOKEN=secret' | |
} | |
} | |
} | |
}) | |
.sortme.must_equal([ | |
{key: 'grouped_requests.example.com.method', value: 'get'}, | |
{key: 'grouped_requests.example.com.headers.cookie', value: 'TOKEN=secret'}, | |
{key: 'grouped_requests.www.example.com.method', value: 'get'}, | |
{key: 'grouped_requests.www.example.com.headers.cookie', value: 'TOKEN=secret'} | |
].sortme) | |
end | |
describe "deeply nested hash/array/scalar" do | |
example = { | |
requests: [ | |
{ | |
method: 'get', | |
headers: [ | |
{host: 'example.com'}, | |
{tracking: 'google'}, | |
{tracking: 'facebook'} | |
] | |
} | |
] | |
} | |
it "must flatten" do | |
FlatTransformer.deep_flatten_jsonpath(example).sortme.must_equal([ | |
{key: 'requests.0.method', value: 'get'}, | |
{key: 'requests.0.headers.0.host', value: 'example.com'}, | |
{key: 'requests.0.headers.1.tracking', value: 'google'}, | |
{key: 'requests.0.headers.2.tracking', value: 'facebook'} | |
].sortme) | |
end | |
it "must flatten until limit reached" do | |
FlatTransformer.deep_flatten_jsonpath(example, 2).sortme.must_equal([ | |
{key: 'requests.0', value: "{:method=>\"get\", :headers=>[{:host=>\"example.com\"}, {:tracking=>\"google\"}, {:tracking=>\"facebook\"}]}"}, | |
].sortme) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FlatTransformer | |
def deep_flatten_jsonpath(root_value, root_key=nil, max_depth=5) | |
if max_depth > 0 && root_value.is_a?(Hash) | |
root_value.reduce([]) do |acc, (key, val)| | |
acc.concat [deep_flatten_jsonpath(val, [root_key, key].compact.join('.'), max_depth - 1)] | |
end.flatten | |
elsif max_depth > 0 && root_value.is_a?(Array) | |
root_value.each_with_index.map do |val, idx| | |
deep_flatten_jsonpath(val, [root_key, idx].compact.join('.'), max_depth - 1) | |
end.flatten | |
else | |
[{key: root_key ? root_key.to_s : 'root', value: root_value.to_s}] | |
end | |
end | |
end |
vstepanyuk
commented
Dec 5, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment