How can I get a single result object in jq? - json

I have a JSON file like this:
{
"cases": [
{"id": "1", "count": 2, "ignore": "f"},
{"id": "2", "count": 7, "ignore": "o"},
{"id": "3", "count": 11, "ignore": "o"}
]
}
Doing jq '.cases[] | { "\(.id)" : .count }' test.json gives
{
"1": 2
}
{
"2": 7
}
{
"3": 11
}
but I need
{
"1": 2,
"2": 7,
"3": 11
}
How can I get there?

You need to collect the results into an array and add them
.cases | map({ "\(.id)" : .count }) | add

Here's an approach using reduce which iteratively builds up the result object:
reduce .cases[] as {$id, $count} ({}; .[$id] = $count)
Demo

from_entries sounds like the obvious choice. First map your input array to an array of key-value pairs, then construct an object from this array:
.cases | map({ key: .id, value: .count }) | from_entries

Related

Using jq to merge two arrays into one array of objects

I am very beginner into jq and I want to reshape my JSON file.
I have ve got JSON structured like this:
{
"a": [1, 2, 3, 4 ...],
"b": [
{
"x": 1000,
"value": 1
},
{
"x": 1000,
"value": 2
},
{
"x": 1000,
"value": 3
}
...
]
}
I am wondering how I can achieve result like this with jq:
[
{
"value": 1,
"from": "a",
},
{
"value": 2,
"from": "a"
},
...
{
"value": 1,
"from": "b"
},
{
"value": 2,
"from": "b"
}
...
]
Here is a very slightly generic, but hardly robust, solution:
map_values( if type == "array"
then map(if type == "object" then .value else . end)
else . end)
| [ keys_unsorted[] as $k
| .[$k][] as $v
| { value: $v, from: $k } ]
Create two lists, one from .a and one from .b, and merge them with +.
In the first list, create objects with value: set to the original content and add from: "a"; in the second list, remove .x from elements of .b and add the from again.
jq '[.a[] | {value:(.), from: "a"}] + [.b[] | del(.x) + {from: "b"}]

How to "number" array items? [duplicate]

This question already has answers here:
How do i add an index in jq
(3 answers)
Closed 2 years ago.
Considering input:
[
{
"a": 1
},
{
"a": 2
},
{
"a": 7
}
]
how do I add new field to each object, which value would be index in array? Producing:
[
{
"a": 1,
"index": 0
},
{
"a": 2,
"index": 1
},
{
"a": 7,
"index": 2
}
]
Using reduce, without disassembling/reassembling the input:
reduce range(length) as $index (.; .[$index] += {$index})
Online demo
Store the structure into a variable, then use keys to get the indices, retrieve the corresponding object from the variable using the index and add the index to it:
jq '[ . as $d | keys[] | $d[.] + {index:.} ]' file.json
to_entries takes an object and returns an array of key/value pairs.
It can be used effectively and intuitively.
jq/to_entries
input file
// file.json
[
{
"a": 1
},
{
"a": 2
},
{
"a": 7
}
]
commands
jq 'to_entries | map(.value+{index:.key})' file.json
results
[
{
"a": 1,
"index": 0
},
{
"a": 2,
"index": 1
},
{
"a": 7,
"index": 2
}
]
Here's also an alternative (non-jq) solution, using jtc:
bash $ <input.json jtc -w'[:]<I>k' -i'{"index":{I}}'
[
{
"a": 1,
"index": 0
},
{
"a": 2,
"index": 1
},
{
"a": 7,
"index": 2
}
]
bash $
PS. I'm a developer of jtc - unix JSON processor
PPS. The disclaimer is required by SO.
Try : Array[index_of_object].property = value
for example : array[0].a = 1

Reshape a jq array with summarized data

New to jq but I've managed to group a load of data which I would now like summarized in a different format. Original data after group by and mapping:
[
{
"Agents": "a",
"Count": 1
},
{
"Agents": "b",
"Count": 50
},
{
"Agents": "c",
"Count": 25
},
{
"Agents": "d",
"Count": 1
},
{
"Agents": "e",
"Count": 4
},
{
"Agents": "f",
"Count": 4
},
{
"Agents": "g",
"Count": 4
}
]
and I would like this output:
{
"Count": 7,
"Agents": {
"a": 1,
"b": 50,
"c": 25,
"d": 1,
"e": 4,
"f": 4,
"g": 4
}
}
How exactly might I do this in jq please because it requires mapping the values as field names?
Another variant using reduce would be to do. The reduce expression takes the array as input and puts the key as the Agents and the value as its corresponding Count value.
jq '{ Count: length, Agents: (reduce .[] as $d ({}; .[$d.Agents] = $d.Count)) }'
The Object Value Iterator .[] used to construct the JSON. For a given .["a"], it returns "a" which is how the keys are constructed in the final JSON.
Use map to create an input for from_entries:
map({key: .Agents, value: .Count}) | {Count: length, Agents: from_entries}
map produces a list of objects like [{"key": "a", "value": 1}, ...]. from_entries turns that into a single object {"a": 1, ...}. In the final object, both length and from_entries get the same array as input, and their outputs are used to create the final object with Count and Agents keys.

jq: extract a specific key from one object to another

I have two JSON files.
file1.json:
{
"Fruits": [
{
"name": "Apple",
"something_else": 123,
"id": 652090
},
{
"name": "Orange",
"something_else": 456,
"id": 28748
}
]}
file2.json:
{
"Fruits": [
{
"weight": 5,
"id": 652090
},
{
"weight": 7,
"id": 28748
}
]}
I want to combine objects from both files if they have a common key 'id', but to extract only 'name' property from file1. How do I do that using jq?
This is what I want to get:
{
"Fruits": [
{
"name": "Apple",
"weight": 5,
"id": 652090
},
{
"name": "Orange",
"weight": 7,
"id": 28748
},
]}
Combine Fruits arrays, group it by id, select groups with 2 elements because we want fruits present in both files. For each selected group; add name field from first group element to second, and collect results in an array.
jq -n '[inputs.Fruits[]]
| reduce (group_by(.id)[] | select(length==2)) as $f
([]; . + [$f[1] + ($f[0] | {name})])' file1.json file2.json
Note that the order files are given on the command line is important, the file with names should be given before the other.
Combining objects with same id and extracting a subset of fields is way much easier though:
jq -n '[inputs.Fruits[]]
| group_by(.id)
| map(select(length==2) | add | {name, id, weight})
' file1.json file2.json
There's plenty of ways this could be constructed. Here's another way:
$ jq '.Fruits |= (. + input.Fruits | [group_by(.id)[] | add | {name,weight,id}])' \
file1.json file2.json
{
"Fruits": [
{
"name": "Orange",
"weight": 7,
"id": 28748
},
{
"name": "Apple",
"weight": 5,
"id": 652090
}
]
}

How can you sort a JSON object efficiently using JQ

I've got JSON in the format
{
"a": {
"size":3
},
"b": {
"size":2
},
"c": {
"size":1
}
}
I need to sort it by size, e.g.:
{
"c": {
"size": 1
},
"b": {
"size": 2
},
"a": {
"size": 3
}
}
I have found a way to do it, e.g.:
. as $in | keys_unsorted | map ({"key": ., "size" : $in[.].size}) | sort_by(.size) | map(.key | {(.) : $in[.]}) | add
but this seems quite complex so I'm hoping there's a simpler way that I've overlooked?
You can use to_entries / from_entries, like this:
jq 'to_entries|sort_by(.value.size)|from_entries' file.json
to_entries will transform your input object into a list of key/value pair objects:
[
{
"key": "a",
"value": {
"size": 3
}
},
...
{
"key": "c",
"value": {
"size": 1
}
}
]
That allows to apply sort_by(.value.size) to that list and then convert it back to an object using from_entries.