'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'

Demo

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'

Demo

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