Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct way to test for the type of a JSON value #75

Closed
tischwa opened this issue Jan 22, 2013 · 4 comments
Closed

Correct way to test for the type of a JSON value #75

tischwa opened this issue Jan 22, 2013 · 4 comments

Comments

@tischwa
Copy link

tischwa commented Jan 22, 2013

Hi,

what is the correct way to test for the type of a value?

Example: I only want to get arrays or objects and unwrap the contained values. If I don't filter out scalars jq gives an error:

% echo '[1, 2] 1' | jq '.[]'
1
2
jq: error: Cannot iterate over number

Now I test for arrays and objects "hijacking" the >= operator:

% echo '[1, 2] 1' | jq 'select(. >= []) | .[]'
1
2

Is there a better way of doing this?

And the general question is: How to test for

  • number
  • string
  • array
  • ...

Regards,
Tilo

@ghost
Copy link

ghost commented Jan 22, 2013

Thanks, i was looking for that! One hack to test for strings is:

echo '[1111] [] {"x":"y"} {} "z" 2222 true false null' | jq 'select(.==tostring)'
"z"

Based on your test for "object or array", just discovered a test for objects only:

echo '[1111] [] {"x":"y"} {} "z" 2222 true false null' | jq 'select(.>={})'
{
  "x": "y"
}
{}

Can combine to test for arrays:

echo '[1111] [] {"x":"y"} {} "z" 2222 true false null' | jq 'select(.>=[] and .<{})'
[
  1111
]
[]

Finally, can test for number by combining "not an object or array", string test and exhaustive explicit tests for not true/false/null:

echo '[1111] [] {"x":"y"} {} "z" 2222 true false null' |
jq 'select(.<[] and .!=tostring and .!=true and .!=false and .!=null)'
2222

Is there a shorter way?

@stedolan
Copy link
Contributor

While not as ingenious as @13ren's solution, you can also use the type function :)

$ echo '[true, null, 42, "hello", []]' | ./jq 'map(type)'
["boolean","null","number","string","array"]

@tischwa
Copy link
Author

tischwa commented Jan 23, 2013

On Wed, 23 Jan 2013 12:29:04 +0100, Stephen Dolan
notifications@github.com wrote:

While not as ingenious as @13ren's solution, you can also use the type
function :)

$ echo '[true, null, 42, "hello", []]' | ./jq 'map(type)'
["boolean","null","number","string","array"]

Great! Exactly what I was looking for.

I did search http://stedolan.github.com/jq/manual for such a function but
didn't find anything.

This function is probably useful for others too.

Regards,

Tilo

@ghost
Copy link

ghost commented Jan 24, 2013

This is great! It enables recurse(filter) to traverse arbitrary json (i.e. without an explicit field holding child values, unlike the reddit json in #37 which inspired it).

echo '[true, null, 42, "hello", []]' | jq 'recurse(.[])'
[
  true,
  null,
  42,
  "hello",
  []
]
true
jq: error: Cannot iterate over boolean
42
jq: error: Cannot iterate over number
"hello"
jq: error: Cannot iterate over string
[]
echo '[true, null, 42, "hello", []]' | jq 'recurse(select(type|.=="array"or.=="object") | .[])'
[
  true,
  null,
  42,
  "hello",
  []
]
true
42
"hello"
[]

NB: recurse omits nulls (due to its definition), and Tilo's sweet hack recurse( select(.>=[]) | .[] ) is shorter.

Would it make sense to include something like the above, as a builtin traverse(filter)? It seems an important special-case of recursion for JSON.

Aside: recurse can (of course) do general recursion e.g.

jq -n '0 | recurse( .+1|select(.<3) )'
0
1
2

Sorry, getting carried away here, but we can define a for-loop function, with optional step:

def for(args): args as $a| ($a[2] // 1) as $step|   $a[0] | recurse( .+$step| select(.<$a[1]) );
for([0,4,2])
0
2
for([0,4])
0
1
2
3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants