给定条目格式的输入,可能具有重复的键,例如
[
{"key": "a", "value": 0},
{"key": "a", "value": 1},
{"key": "a", "value": 2},
{"key": "b", "value": 3},
{"key": "b", "value": 4},
{"key": "b", "value": 5}
]
我想生产
{"a": [0, 1, 2], "b": [3, 4, 5]}
即,针对每个唯一键将具有相同键的所有值收集在数组中。
我怎样才能做到这一点jq
?
答案1
使用reduce()
in通过将给定值添加到值给定的键来jq
逐渐构建结果对象:value
key
$ jq -c 'reduce .[] as $a ({}; .[$a.key] += [$a.value])' file
{"a":[0,1,2],"b":[3,4,5]}
其reduce()
行为就像一种循环,在本例中,它迭代.[]
,即顶级数组中的所有对象。对于每个对象$a
,值都会添加到键 下的$a.value
结果对象(最初是空对象)中。{}
$a.key
对于喜欢使用 的人来说,还有一个提示jq
:通过使用debug
,您可以在表达式运行时查看表达式中任意点的数据。在这里,我正在研究循环中每次迭代后累加器对象的状态reduce()
:
$ jq -c 'reduce .[] as $a ({}; .[$a.key] += [$a.value] | debug)' file
["DEBUG:",{"a":[0]}]
["DEBUG:",{"a":[0,1]}]
["DEBUG:",{"a":[0,1,2]}]
["DEBUG:",{"a":[0,1,2],"b":[3]}]
["DEBUG:",{"a":[0,1,2],"b":[3,4]}]
["DEBUG:",{"a":[0,1,2],"b":[3,4,5]}]
{"a":[0,1,2],"b":[3,4,5]}
$a
在这里,我正在研究每次迭代中分配的值:
$ jq -c 'reduce (.[]|debug) as $a ({}; .[$a.key] += [$a.value])' file
["DEBUG:",{"key":"a","value":0}]
["DEBUG:",{"key":"a","value":1}]
["DEBUG:",{"key":"a","value":2}]
["DEBUG:",{"key":"b","value":3}]
["DEBUG:",{"key":"b","value":4}]
["DEBUG:",{"key":"b","value":5}]
{"a":[0,1,2],"b":[3,4,5]}
答案2
您可以使用group_by
,map
和from_entries
jq 'group_by(.key) | map({key: .[0].key, value: [.[].value]}) | from_entries' data.json
答案3
如果您已经了解一种编程语言,那么这是一个简单的选择,而不必使用非常特定的jq
语言。和perl
:
$ perl -MJSON -l -0777 -ne '
for (@{decode_json$_}) {push @{$out->{$_->{key}}}, $_->{value}}
print encode_json $out' your-file.json
{"a":[0,1,2],"b":[3,4,5]}
答案4
使用乐(以前称为 Perl_6)
您需要使用 JSON 解析器:
~$ raku -MJSON::Tiny -e 'my @json = from-json($_).list given slurp; \
my %accum.push: .<key> => .<value>.map(*.Num).Slip for @json>>.split(", "); \
.say for to-json(%accum.sort);' file
输入示例:
[
{"key": "a", "value": 0},
{"key": "a", "value": 1},
{"key": "a", "value": 2},
{"key": "b", "value": 3},
{"key": "b", "value": 4},
{"key": "b", "value": 5}
]
示例输出(值已被Num
-ified 和Slip
ped,即展平):
[ { "a" : [ 0, 1, 2 ] }, { "b" : [ 3, 4, 5 ] } ]
最终代码to-json()
在末尾添加了一个调用。要弄清楚中间步骤中发生了什么,最简单的方法可能是向您显示每个单独数据结构的输出。打印@json
数组会.say for @json;
返回以下内容:
{key => a, value => 0}
{key => a, value => 1}
{key => a, value => 2}
{key => b, value => 3}
{key => b, value => 4}
{key => b, value => 5}
%accum
仅使用 .<key> => .<value>
(换句话说,不使用)生成哈希.map(*.Num).Slip
通过调用返回以下内容.say for %accum.sort;
:
[ { "a" : [ [ "0" ], [ "1" ], [ "2" ] ] }, { "b" : [ [ "3" ], [ "4" ], [ "5" ] ] } ]
Num
请注意,您可以使用、等函数调用对值进行各种操作Int
,并且可以通过调用Slip
如顶部发布的代码来“展平”。
Raku 的JSON::Tiny
模块默认会对值进行字符串化,如果您希望这些字符串化值作为单个列表(而不是子列表),请将Slip
它们放在一起。
下面,字符串化的值Slip
组合在一起:
~$ raku -MJSON::Tiny -e 'my @json = from-json($_).list given slurp; \
my %accum.push: .<key> => .<value>.Slip for @json>>.split(", "); \
.say for to-json(%accum.sort);' file
[ { "a" : [ "0", "1", "2" ] }, { "b" : [ "3", "4", "5" ] } ]
因此,这里有很多用于数据输出的选项,最简单的是采用顶部的代码并删除最终的to-json()
调用,以查看 Raku 的%accum
哈希的“对”表示:
a => [0 1 2]
b => [3 4 5]