5

I want to get the value after a selected item in a list in jq.

Something like this seems like it would work:

jq '. as $list | select(.focused == true) | .id as $id | $list | select(????) 

One way of doing this would be to enumerate entries like Python - is there a way of doing this. In python enumerate takes [x0, x1, x2, x3, ...] -> [(0, x0), (1, x1), ...] . Another way would be an equivalent of itertools itertools.dropwhile

2 Answers 2

7

to_entries gives a similar result when its input is an array.

$ echo '["zero", "one", "two", "three"]' | jq 'to_entries'
[
  { "key": 0, "value": "zero" },
  { "key": 1, "value": "one" },
  { "key": 2, "value": "two" },
  { "key": 3, "value": "three" }
]

$ echo ... | jq 'to_entries[] | select(.value == "two").key'
2

Possible other approach:

$ echo ... | jq '. as $list | range(length) | $list[.:.+2]'
[ "zero", "one" ]
[ "one", "two" ]
[ "two", "three" ]
[ "three" ]

$ echo ... | jq '. as $list | range(length) | $list[.:.+2] | select(.[0] == "one")[1]'
"two"
3
$ jq -n '$ARGS.positional as $list | $list[$list | indices("3")[] + 1]' --args 3 1 2 3 A B C 3
"1"
"A"
null

The command returns the elements after any element in the input that is 3. In the example, the input is the list of strings given at the end of the command line, and the output shows that the strings 1 and A occur after the string 3 in the input. There is also a null value outputted as there is a 3 at the very end of the input list.

The command works by using indices() to return a list of indices of values equal to 3, adding 1 to each of them, and using the result as an index into the original list.

The following is a reformulation of the above on a form similar to that in the question:

jq -n '$ARGS.positional as $list | ($list | indices("3")[]) as $id | $list[$id + 1]' --args 3 1 2 3 A B C 3

The ... as $id is a loop over the indices that correspond to values that are 3 in the input list. The body of the loop, $list[$id + 1], extracts the value after the given index.

By moving the array expansion from indices("3")[] to where $id is used, we turn $id into an array, and the "loop" is demoted into a multi-valued index instead:

jq -n '$ARGS.positional as $list | ($list | indices("3")) as $id_list | $list[$id_list[] + 1]' --args 3 1 2 3 A B C 3

With an array of objects as input, we would need to compare with an object too:

$ cat file
[
    {"thing":"3"},
    {"thing":"1"},
    {"thing":"2"},
    {"thing":"3"},
    {"thing":"A"},
    {"thing":"B"},
    {"thing":"C"},
    {"thing":"3"}
]
$ jq '. as $list | (indices({thing: "3"})) as $id_list | $list[$id_list[] + 1]' file
{
  "thing": "1"
}
{
  "thing": "A"
}
null

As you may imagine, this becomes a bit unwieldy if the array entries are complex and you only care for smaller parts of them; it's difficult to generalise.

This is fixed by extracting the interesting bits and using indices() on them (and at the same time, I'm dropping the creation of the variable $id_list, as it's strictly not needed):

$ cat file
[
    {"thing":"3", "other bits": "unimportant"},
    {"thing":"1", "other bits": "unimportant"},
    {"thing":"2", "other bits": "unimportant"},
    {"thing":"3", "other bits": "unimportant"},
    {"thing":"A", "other bits": "unimportant"},
    {"thing":"B", "other bits": "unimportant"},
    {"thing":"C", "other bits": "unimportant"},
    {"thing":"3", "other bits": "unimportant"}
]
$ jq '. as $list | map_values(.thing == "3") | indices(true) | $list[.[] + 1]' file
{
  "thing": "1",
  "other bits": "unimportant"
}
{
  "thing": "A",
  "other bits": "unimportant"
}
null

Then you'll do what every programmer would do, which is to abstract away the details into a function:

def select_indices(f): map_values(f) | indices(true);

... and call that instead

. as $list | select_indices(.thing == "3") | $list[.[] + 1]
0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.