Skip to content

Instantly share code, notes, and snippets.

@bsingr
Last active December 6, 2019 09:40
Show Gist options
  • Save bsingr/b12f0e04284bc3ef0a1142e4e6944522 to your computer and use it in GitHub Desktop.
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
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
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
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
Copy link

const queue = [];

function flatten(data) {
  queue.push({ path: [], value: data });

  const result = [];
  while (queue.length) {
    const next = queue.shift();
    const { value, path } = next;

    if (Array.isArray(value)) {
      value.forEach((value, index) => queue.push({
        path: path.concat(index),
        value
      }))
    } else if (typeof(value) === 'object') {
      Object.entries(value).forEach(([ key, value ]) => queue.push({
        path: path.concat(key),
        value
      }));
    } else {
      result.push({
        key: path.join('.'),
        value
      })
    }
  }

  return result;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment