output arrays values as single object - json

I need to be able to produce the following but without having to explicit array's indexes, so that I don't need to know input array's lenght
echo '[{"name":"John", "age":30, "car":null},{"name":"Marc", "age":32, "car":null}]' | jq -r '{(.[0].name):.[0].age,(.[1].name):.[1].age}'
Produces :
{ "John": 30, "Marc": 32}

Use add to merge the objects.
jq '[ .[] | { (.name) : .age } ] | add'

Related

jq - Looping through json and concatenate the output to single string

I was currently learning the usage of jq. I have a json file and I am able to loop through and filter out the values I need from the json. However, I am running into issue when I try to combine the output into single string instead of having the output in multiple lines.
File svcs.json:
[
{
"name": "svc-A",
"run" : "True"
},
{
"name": "svc-B",
"run" : "False"
},
{
"name": "svc-C",
"run" : "True"
}
]
I was using the jq to filter to output the service names with run value as True
jq -r '.[] | select(.run=="True") | .name ' svcs.json
I was getting the output as follows:
svc-A
svc-C
I was looking to get the output as single string separated by commas.
Expected Output:
"svc-A,svc-C"
I tried to using join, but was unable to get it to work so far.
The .[] expression explodes the array into a stream of its elements. You'll need to collect the transformed stream (the names) back into an array. Then you can use the #csv filter for the final output
$ jq -r '[ .[] | select(.run=="True") | .name ] | #csv' svcs.json
"svc-A","svc-C"
But here's where map comes in handy to operate on an array's elements:
$ jq -r 'map(select(.run=="True") | .name) | #csv' svcs.json
"svc-A","svc-C"
Keep the array using map instead of decomposing it with .[], then join with a glue string:
jq -r 'map(select(.run=="True") | .name) | join(",")' svcs.json
svc-A,svc-C
Demo
If your goal is to create a CSV output, there is a special #csv command taking care of quoting, escaping etc.
jq -r 'map(select(.run=="True") | .name) | #csv' svcs.json
"svc-A","svc-C"
Demo

Merge 2 files which have Json objects using Jq

I have a requirement where in 2 parameter files needs to be merged to one using Jq
param1.json
[
"name=xyz",
"age=40",
"email=qqqq"
]
param2.json
[
"name=xyz",
"age=42",
"drivingLicense=2761"
]
I need a resultant value to be
[
"name=xyz",
"age=42",
"email=qqqq",
"drivingLicense=2761"
]
When I try to use Jq add jq -s '.[0] + .[1]' param1.json param2.json the resultant
[
"name=xyz",
"age=40",
"email=qqqq",
"name=xyz",
"age=42",
"drivingLicense=2761"
]
I tried using jq '. * input' param1.json param2.json but that is not working either
What is the best way to merge them
TIA
This approach makes use of the circumstance that object field names are unique. On collision, latter items overwrite former ones.
jq -s '[add | with_entries(.key = (.value | .[:index("=")]))[]]'
[
"name=xyz",
"age=42",
"email=qqqq",
"drivingLicense=2761"
]
Demo
Note: Instead of add you can, of course, still use .[0] + .[1] or . + input (the latter without -s).
You can first convert your arrays into objects, then add those objects together; then convert to an array again:
$ jq -s 'map(map(./"="|{(first):.[1:]|join("=")})|add)|add|to_entries|map(join("="))' param1.json param2.json
[
"name=xyz",
"age=42",
"email=qqqq",
"drivingLicense=2761"
]
If your values cannot contain an equal sign, then {(first):.[1:]|join("=")} can be simplified to {(first):last}.
Or merging the arrays to one big array before converting to objects:
add
|map(./"="|{(first):.[1:]|join("=")})
|add
|to_entries
|map(join("="))
Levaraging the fact that this can be reformulated as a grouping problem, you can group by the "key" of your string, then select the last item in each group (A reusable function to build group objects can help but is not required).
$ jq -s 'add | map(./"=") | group_by(first) | map(last|join("="))' param1.json param2.json
[
"age=42",
"drivingLicense=2761",
"email=qqqq",
"name=xyz"
]

jq how to pass json keys from a shell variable

I have a json file I am parsing with jq. This is a sample of the file
[{
"key1":{...},
"key2":{...}
}]
[{
"key1":{...},
"key2":{...}
}]
...
each line is a list containing a json (which I know is not technically a json format but jq still works on such a file)
The below jq command works:
cat file.json | jq -r '.[] | [.key1,.key2]'
The above correctly shows:
[
<value_of_key1>,<value_of_key2>
]
[
<value_of_key1>,<value_of_key2>
]
However, I want .key1,.key2 to be dynamic since these keys can change. So I want to pass a variable to jq. Something like:
$KEYS=.key1,.key2
cat file.json | jq -r --arg var "$KEYS" '.[] | [$var]'
But the above is returning the keys themselves:
[
".key1,.key2"
]
[
".key1,.key2"
]
why is this happening? what is the correct command to make this happen?
This answer does not help me. I am not getting any errors as the OP in that question.
Fetching the value of a jq variable doesn't cause it to be executed as jq code.
Furthermore, jq lacks the facility to take a string, compile it as jq code, and evaluate the result. (This is commonly known as eval.)
So, short of a writing a jq parser and evaluator in jq, you will need to impose limits and/or accept a different format.
For example,
keys='[ [ "key1", "childkey" ], [ "key2", "childkey2" ] ]' # JSON
jq --argjson keys "$keys" '.[] | [ getpath( $keys[] ) ]' file.json
or
keys='key1.childkey,key2.childkey2'
jq --arg keys "$keys" '
( ( $keys / "," ) | map( . / "." ) ) as $keys |
.[] | [ getpath( $keys[] ) ]
' file.json
Suppose you have:
cat file
[{
"key1":1,
"key2":2
}]
[{
"key1":1,
"key2":2
}]
You can use a jq command like so:
jq '.[] | [.key1,.key2]' file
[
1,
2
]
[
1,
2
]
You can use -f to execute a filter from a file and nothing keeps you from creating the file separately from the shell variables.
Example:
keys=".key1"
echo ".[] | [${keys}]" >jqf
jq -f jqf file
[
1
]
[
1
]
Or just build the string directly into jq:
# note double " causing string interpolation
jq ".[] | [${keys}]" file
You can use --argjson option and destructuring.
file.json
[{"key1":{"a":1},"key2":{"b":2}}]
[{"key1":{"c":1},"key2":{"d":2}}]
$ in='["key1","key2"]' jq -c --argjson keys "$in" '$keys as [$key1,$key2] | .[] | [.[$key1,$key2]]' file.json
output:
[{"a":1},{"b":2}]
[{"c":1},{"d":2}]
Elaborating on ikegami's answer.
To start with here's my version of the answer:
$ in='key1.a,key2.b'; jq -c --arg keys "$in" '($keys/","|map(./".")) as $paths | .[] | [getpath($paths[])]' <<<$'[{"key1":{"a":1},"key2":{"b":2}}] [{"key1":{"a":3},"key2":{"b":4}}]'
This gives output
[1,2]
[3,4]
Let's try it.
We have input
[{"key1":{"a":1},"key2":{"b":2}}]
[{"key1":{"a":3},"key2":{"b":4}}]
And we want to construct array
[["key1","a"],["key2","b"]]
then use it on getpath(PATHS) builtin to extract values out of our input.
To start with we are given in shell variable with string value key1.a,key2.b. Let's call this $keys.
Then $keys/"," gives
["key1.a","key2.b"]
["key1.a","key2.b"]
After that $keys/","|map(./".") gives what we want.
[["key1","a"],["key2","b"]]
[["key1","a"],["key2","b"]]
Let's call this $paths.
Now if we do .[]|[getpath($paths[])] we get the values from our input equivalent to
[.[] | .key1.a, .key2.b]
which is
[1,2]
[3,4]

How to access the value of an unknown key?

I have the following JSON file
{
"https://test.com/gg": [
"msg",
"popup",
"url"
]
}
What I want to achieve is to parse the values to output the following
https://test.com/gg?msg=gg
https://test.com/gg?popup=gg
https://test.com/gg?url=gg
I'm assuming it can be done using jq but I'm not sure how.
The way i know is if the elemets were named like bellow:
{
"url":"https://test.com/gg": [
"p1":"msg",
]
}
I would pull the elements like:
cat json | jq "url.[p1]"
But in my case is it not named.
jq --raw-output 'to_entries[0] | .key as $url | .value[] | "\($url)?\(.)=gg"' <your json file here>
Where
to_entries[0] yields {"key":"https://test.com/gg","value":["msg","popup","url"]}
(Save .key as $url for later)
Then "emit" all values with .value[]
For each "emitted" value, produce the string "\($url)?\(.)=gg" where . is the current value

add values to array of keys with JQ

I have a simple JSON array:
[
"smoke-tests",
"other-tests"
]
I'd like to convert to a simple JSON:
{"smoke-tests": true,
"other-tests": true
}
I've tried several jq examples, but none seem to do what I want.
jq '.[] | walk(.key = true)' produces a compile error.
If you like the efficiency of reduce but don't want to use reduce explicitly:
. as $in | {} | .[$in[]] = true
$ s='["smoke-tests", "other-tests"]'
$ jq '[.[] | {(.): true}] | add' <<<"$s"
{
"smoke-tests": true,
"other-tests": true
}
Breaking down how that works: .[] | {(.): true} converts each item into a dictionary mapping the value (as a key) to true. Surrounding that in [ ] means we generate a list of such objects; sending that to add combines them into a single object.
Here is a solution using add. It's close to Charles's solution but uses the behavior of Object construction to implicitly return multiple objects when used with an expression which returns multiple results.
[{(.[]):true}]|add
With reduce() function:
jq 'reduce .[] as $k ({}; .[$k]=true)' file
The output:
{
"smoke-tests": true,
"other-tests": true
}