Javascript 选择性地展平嵌套JSON结构
所以这是一个问题,我甚至不知道从哪里开始,所以即使只是一个指向正确方向的指针也很好 所以我有这样的数据:Javascript 选择性地展平嵌套JSON结构,javascript,python,json,
elasticsearch,clojure,Javascript,Python,Json,
elasticsearch,Clojure,所以这是一个问题,我甚至不知道从哪里开始,所以即使只是一个指向正确方向的指针也很好 所以我有这样的数据: data = { "agg": { "agg1": [ { "keyWeWant": "*-20.0", "asdf": 0, "asdf": 20, "asdf": 14, "some_nested_agg": [
data = {
"agg": {
"agg1": [
{
"keyWeWant": "*-20.0",
"asdf": 0,
"asdf": 20,
"asdf": 14,
"some_nested_agg": [
{
"keyWeWant2": 20,
"to": 25,
"doc_count": 4,
"some_nested_agg2": {
"count": 7,
"min": 2,
"max": 5,
"keyWeWant3": 2.857142857142857,
"sum": 20
}
},
{
"keyWeWant2": 25,
"to": 30,
"doc_count": 10,
"some_nested_agg2": {
"count": 16,
"min": 2,
"max": 10,
"keyWeWant3": 6.375,
"sum": 102
}
}
]
},
{
...
},
{
...
},
...
]
}
}
现在从示例中可以看出,在“agg”中有N个“agg1”结果,在每个“agg1”结果中有一个“keyWeWant”。每个“agg1”结果也有一个“一些嵌套的”结果列表,每个结果都包含一个“keyWeWant2”。每个“keyWeWant2”值都与层次结构中某个位置的单个“keyWeWant”值相关联。类似地,每个“keyWeWant2”也包含“some_nested_agg2”的一组结果(这次不是列表而是映射)。每个结果集都包含一个“keyWeWant3”
现在,我想在保持“keyWeWant”、“keyWeWant2”和“keyWeWant3”(我基本上是去规范化)之间的关联的同时,扁平化这个结构,得到如下结果:
我希望函数看起来像什么:
[
{
"keyWeWant" : "*-20",
"keyWeWant2" : 20,
"keyWeWant3" : 2.857142857142857
},
{
"keyWeWant" : "*-20",
"keyWeWant2" : 25,
"keyWeWant3" : 6.375
},
{
...
},
{
...
}
]
这是一个示例,其中只有深度3,但可以有任意深度,其中一些嵌套值为列表,一些嵌套值为数组/列表
我想做的是编写一个函数,接收我想要的键以及在哪里找到它们,然后获取键并反规范化
看起来像:
function_name(data_map, {
"keyWeWant" : ['agg', 'agg1'],
"keyWeWant2" : ['agg', 'agg1', 'some_nested_agg'],
"keyWeWant" : ['agg', 'agg1', 'some_nested_agg', 'some_nested_agg2']
})
有什么想法吗?我熟悉Java、Clojure、Java脚本和Python,我只是在寻找一种相对简单的方法来解决这个问题。可能有更好的方法来解决这个特定问题(使用一些ElasticSearch库或其他东西),但这里有一个Clojure中的解决方案,使用您请求的输入和输出数据格式 我将此测试数据放在一个名为
data.json的文件中:
{
"agg": {
"agg1": [
{
"keyWeWant": "*-20.0",
"asdf": 0,
"asdf": 20,
"asdf": 14,
"some_nested_agg": [
{
"keyWeWant2": 20,
"to": 25,
"doc_count": 4,
"some_nested_agg2": {
"count": 7,
"min": 2,
"max": 5,
"keyWeWant3": 2.857142857142857,
"sum": 20
}
},
{
"keyWeWant2": 25,
"to": 30,
"doc_count": 10,
"some_nested_agg2": {
"count": 16,
"min": 2,
"max": 10,
"keyWeWant3": 6.375,
"sum": 102
}
}]
}]}
}
然后将数据解析为Clojure数据结构:
(use '[cheshire.core :as cheshire])
(def my-data (-> "data.json" slurp cheshire/parse-string))
接下来,要获取的路径定义如下:
(def my-data-map
{"keyWeWant" ["agg", "agg1"],
"keyWeWant2" ["agg", "agg1", "some_nested_agg"],
"keyWeWant3" ["agg", "agg1", "some_nested_agg", "some_nested_agg2"]})
这是您的数据图
上面没有“:”,单引号改为双引号,最后一个“keyWeWant”改为“keyWeWant3”
下面的find nested
具有Clojure的语义,只有这样它才能在带有向量的映射上工作,并返回所有值而不是一个值。
当给find nested
一个搜索向量时,它会在嵌套映射中查找所有值,其中一些值可以由一个向量和一个映射列表组成。将检查向量中的每个贴图
(defn find-nested
"Finds all values in a coll consisting of maps and vectors.
All values are returned in a tree structure:
i.e, in your problem it returns (20 25) if you call it with
(find-nested ['agg', 'agg1', 'some_nested_agg', 'keyWeWant2']
my-data).
Returns nil if not found."
[ks c]
(let [k (first ks)]
(cond (nil? k) c
(map? c) (find-nested (rest ks) (get c k))
(vector? c) (if-let [e (-> c first (get k))]
(if (string? e) e ; do not map over chars in str
(map (partial find-nested (rest ks)) e))
(find-nested ks (into [] (rest c)))) ; create vec again
:else nil)))
find nested
查找搜索路径的值:
(find-nested ["agg", "agg1", "some_nested_agg", "keyWeWant2"] my-data)
; => (20 25)
如果指向“keyWeWant”的所有路径都映射到my data
上,则这些是树的切片
:
(*-20.0
(20-25)
(2.857142857142857 6.375))
可以从函数名
中的树
中获得您要求的结构(所有最终结果都有路径),如下所示:
(defn function-name
"Transforms data d by finding (nested keys) via data-map m in d and
flattening the structure."
[d m]
(let [tree (map #(find-nested (conj (second %) (first %)) d) m)
leaves (last tree)
leaf-indices (range (count leaves))
results (for [index leaf-indices]
(map (fn [slice]
(if (string? slice)
slice
(loop [node (nth slice index)]
(if node
node
(recur (nth slice (dec index)))))))
tree))
results-with-paths (mapv #(zipmap (keys m) %) results)
json (cheshire/encode results-with-paths)]
json))
results
如果叶索引
大于该特定切片,则使用a后退。我认为它也适用于更深层的嵌套结构-如果下一个切片的大小始终是前一个切片的两倍或它应该计算的大小-但我没有测试它
调用(函数名
我的数据
我的数据映射
)将生成所需格式的JSON字符串:
[{
“keyWeWant”:“-20.0”,
“keyWeWant2”:20,
“keyWeWant3”:2.857142857142857}
{
“keyWeWant”:“-20.0”,
“keyWeWant2”25,
“keyWeWant3”6.375}]
/编辑
我知道您正在寻找一个相对简单的解决方案,但这不是。:-)可能有一个没有在库中提供的解决方案。我很高兴了解如何简化它。可能有更好的方法来解决这个特定问题(使用一些ElasticSearch库或其他东西),但这里有一个Clojure中的解决方案,使用您请求的输入和输出数据格式
我将此测试数据放在一个名为data.json的文件中:
{
"agg": {
"agg1": [
{
"keyWeWant": "*-20.0",
"asdf": 0,
"asdf": 20,
"asdf": 14,
"some_nested_agg": [
{
"keyWeWant2": 20,
"to": 25,
"doc_count": 4,
"some_nested_agg2": {
"count": 7,
"min": 2,
"max": 5,
"keyWeWant3": 2.857142857142857,
"sum": 20
}
},
{
"keyWeWant2": 25,
"to": 30,
"doc_count": 10,
"some_nested_agg2": {
"count": 16,
"min": 2,
"max": 10,
"keyWeWant3": 6.375,
"sum": 102
}
}]
}]}
}
然后将数据解析为Clojure数据结构:
(use '[cheshire.core :as cheshire])
(def my-data (-> "data.json" slurp cheshire/parse-string))
接下来,要获取的路径定义如下:
(def my-data-map
{"keyWeWant" ["agg", "agg1"],
"keyWeWant2" ["agg", "agg1", "some_nested_agg"],
"keyWeWant3" ["agg", "agg1", "some_nested_agg", "some_nested_agg2"]})
这是您的数据图
上面没有“:”,单引号改为双引号,最后一个“keyWeWant”改为“keyWeWant3”
下面的find nested
具有Clojure的语义,只有这样它才能在带有向量的映射上工作,并返回所有值而不是一个值。
当find nested
被赋予一个搜索向量时,它会在一个嵌套映射中查找所有值,其中一些值可以由一个带有映射列表的向量组成。向量中的每个映射都会被检查
(defn find-nested
"Finds all values in a coll consisting of maps and vectors.
All values are returned in a tree structure:
i.e, in your problem it returns (20 25) if you call it with
(find-nested ['agg', 'agg1', 'some_nested_agg', 'keyWeWant2']
my-data).
Returns nil if not found."
[ks c]
(let [k (first ks)]
(cond (nil? k) c
(map? c) (find-nested (rest ks) (get c k))
(vector? c) (if-let [e (-> c first (get k))]
(if (string? e) e ; do not map over chars in str
(map (partial find-nested (rest ks)) e))
(find-nested ks (into [] (rest c)))) ; create vec again
:else nil)))
find nested
查找搜索路径的值:
(find-nested ["agg", "agg1", "some_nested_agg", "keyWeWant2"] my-data)
; => (20 25)
如果指向“keyWeWant”的所有路径都映射到my data
上,则这些是树的切片
:
(*-20.0
(20-25)
(2.857142857142857 6.375))
可以从函数名
中的树
中获得您要求的结构(所有最终结果都有路径),如下所示:
(defn function-name
"Transforms data d by finding (nested keys) via data-map m in d and
flattening the structure."
[d m]
(let [tree (map #(find-nested (conj (second %) (first %)) d) m)
leaves (last tree)
leaf-indices (range (count leaves))
results (for [index leaf-indices]
(map (fn [slice]
(if (string? slice)
slice
(loop [node (nth slice index)]
(if node
node
(recur (nth slice (dec index)))))))
tree))
results-with-paths (mapv #(zipmap (keys m) %) results)
json (cheshire/encode results-with-paths)]
json))
结果
如果叶索引
大于该特定切片,则使用。我认为它也适用于更深层的嵌套结构——如果下一个切片的大小总是前一个切片的两倍,或者与它应该计算的大小相同——但我没有测试它
调用(函数名
我的数据
我的数据映射
)将生成所需格式的JSON字符串:
[{
“keyWeWant”:“-20.0”,
“keyWeWant2”:20,
“keyWeWant3”:2.857142857142857}
{
“keyWeWant”:“-20.0”,
“keyWeWant2”25,
“keyWeWant3”6.375}]
/编辑
我知道您正在寻找一个相对简单的解决方案,但事实并非如此。:-)也许有一个图书馆里没有。我很高兴了解如何简化它。这里有一个JavaScript(ES6)函数,您可以使用:
函数展平(数据、键){
var key=key[0];
如果(输入数据)
keys=keys.slice(1);
var res=keys.length&&Object.keys(数据)
.map(key=>data[key])