Debanjan Basu
Debanjan Basu

Reputation: 894

Inconsistent behaviour for jq in/1

The following example from the jq docs worked as expected --

$ echo '[1,2]' | jq 'map(in([0,1]))'
[
  true,
  false
]

but making the values into strings failed -

$ echo '["1", "2"]' | jq 'map(in(["0","1"]))'
jq: error (at <stdin>:1): Cannot check whether array has a string key

To make sure that this wasn't a shell-quoting issue, I also tried --

$ echo "[\"1\", \"2\"]" | jq 'map(in(["0","1"]))'
jq: error (at <stdin>:1): Cannot check whether array has a string key

To add to this, the following snippet also fails, which is probably due to a casting of 0 into the boolean false -

$ echo '[0,1]' | jq 'map(in([1,2]))'       
[
  true,
  true
]

Upvotes: 1

Views: 935

Answers (3)

Summer-Sky
Summer-Sky

Reputation: 491

actually inside does seem to do the trick (apparently added in v1.5)

jq -n '[123,444,565,344,"333",111,1111,87] | map([.]|inside([111,333,344]))'

produces

[
  false,
  false,
  false,
  true,
  false,
  true,
  false,
  false
]

Upvotes: 0

peak
peak

Reputation: 116957

index/1 is the builtin that can most conveniently be used for checking array membership. Although it is not strictly boolean, the semantics of if and // make it convenient to use as though it were. If you want a strictly boolean test, then simply use the idiom index(_) != null.

index/1 is worth understanding in detail, so consider reviewing the jq manual and FAQ accordingly. Note also that INDEX/1 is no relation.

Upvotes: 2

Jeff Mercado
Jeff Mercado

Reputation: 134571

You're misunderstanding what in/1 is doing. It's not testing whether a value is a member of the array, but rather if the value is a member of the object's keys. So for arrays, numbers are expected (the indices) or for objects, strings (the keys). It's exactly the same as has/1 but the inputs are swapped.

$ jq -n '[0,-1,20,9,16,10] | map(in([range(20;30)]))' # 10 item array
[
  true,
  false,
  false,
  true,
  false,
  false
]
$ jq -n '["foo","bar"] | map(in({foo:1,boo:2,bat:3}))'
[
  true,
  false
]

If you wanted to test membership in the array, there isn't a builtin but you could craft it yourself. Though contains/1 is close but not quite since it tests if strings are substrings and not equal. any/2 could be used to do this.

def has_value($value):
    any(.[]; . == $value);

def in_array($arr): # swapped input version
    . as $value | any($arr[]; . == $value);

Upvotes: 6

Related Questions