'strip all but the first element of an object with jq
Assume I have a json structure like:
{
"a" : 1,
"b" : 2,
"c" : 3
}
I want the same structure, but with only the first element:
{
"a" : 1
}
I don't know or care what the name of the key is. I just want the first one. (Bonus points for being able to grab an arbitrary nth one.)
How do I do this with jq?
If I did know the name, this would be easy:
% echo '{"a":1,"b":2,"c":3}' | jq '{a}'
{
"a": 1
}
Which is a shortcut for:
% echo '{"a":1,"b":2,"c":3}' | jq '{a: .a}'
{
"a": 1
}
Working, but terrible, solution
In trying to come up with a bunch of examples for what I've tried that didn't work, I finally came up with this thing that does work:
% echo '{"a":1,"b":2,"c":3}' | jq '{(keys|first):.[(keys|first)]}'
{
"a": 1
}
but that cannot possibly be the best way to do this.
Notably, the "shortcut" doesn't work:
% echo '{"a":1,"b":2,"c":3}' | jq '{(keys|first)}'
jq: error: syntax error, unexpected '}', expecting ':' (Unix shell quoting issues?) at <top-level>, line 1:
{(keys|first)}
jq: 1 compile error
This also works, but is barely better, if not worse:
% echo '{"a":1,"b":2,"c":3}' | jq '(keys|first) as $i | {($i): .[$i]}'
{
"a": 1
}
(The "shortcut" doesn't work here, either.)
WTF?
Also, if I don't use parens around the $i as the key, craziness happens that I don't understand at all:
% echo '{"a":1,"b":2,"c":3}' | jq '(keys|first) as $i | {$i: .[$i]}'
jq: error: syntax error, unexpected ':', expecting '}' (Unix shell quoting issues?) at <top-level>, line 1:
(keys|first) as $i | {$i: .[$i]}
jq: error: May need parentheses around object key expression at <top-level>, line 1:
(keys|first) as $i | {$i: .[$i]}
jq: 2 compile errors
% echo '{"a":1,"b":2,"c":3}' | jq '(keys|first) as $i | {$i}'
{
"i": "a"
}
Other stuff that doesn't work
% echo '{"a":1,"b":2,"c":3}' | jq 'first'
jq: error (at <stdin>:1): Cannot index object with number
% echo '{"a":1,"b":2,"c":3}' | jq 'first(.)'
{
"a": 1,
"b": 2,
"c": 3
}
% echo '{"a":1,"b":2,"c":3}' | jq 'first(.[])'
1
Solution 1:[1]
Use to_entries, from_entries and an index of your choice.
In the single-element syntax you need to wrap it in an array
jq '[to_entries[0]] | from_entries'
In the range-syntax you need to additionally providde the to-index (which is one higher than the from-index if you want just 1 element):
jq 'to_entries[0:1] | from_entries'
Output:
{
"a": 1
}
Solution 2:[2]
You could use jq's streaming parser, which might actually make sense if the input object were huge.
# counting from 1
# e.g. 2 | nthRun(1,2,2,3; .) #=> 2,2
def nthRun(s; f):
. as $n
| label $out
| foreach s as $x (null;
if . == null then [1, ($x|f)]
elif .[1] == ($x|f) then .
else .[0] += 1 | .[1] = ($x|f)
end;
if .[0] == $n then $x
elif .[0] > $n then break $out
else empty
end);
fromstream(1|nthRun(inputs; .[0][0]), [[null]] )
Invocation example:
jq -nc --stream -f program.jq input.json
For the record, assuming the first key's value is atomic, you could simply write:
jq -nc --stream 'fromstream(input, [[null]])'
The same nthRun can of course also be used to access the n-th key-value pair in the same way. More generally, it can be used without using the --stream option, e.g.:
def firstKeyValue:
fromstream(1|nthRun(tostream),[[null]]);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
