8
\$\begingroup\$

Since jq is the Language Of The Month for September 2021, what general tips do you have for golfing in jq? I'm looking for ideas which can be applied to code-golf problems and which are also at least somewhat specific to jq (e.g. "remove comments" is not an answer).

Please post one tip per answer.

\$\endgroup\$
9
\$\begingroup\$

Use try-// instead of if-then-else

if A then B else C end

The conditional expression is very lengthy and almost never1 worth it. Depending on your condition A and the values B and C there might be short alternatives based on indexing or arithmetic, but a general solution is to use try and //.

try is part of the try-catch exception handler, and returns empty for faulty code if the catch part is omitted.
// is the alternative operator which returns the left hand side if it evaluates to a truthy2 value, and the right hand side otherwise.

Putting these two together we can rewrite the conditional expression as:

(try(A//C)*1//B)

The *1 assumes C is a number or a string, for lists you might need +[] instead and if C is a stream of multiple values, this gets a bit trickier. The outer parentheses can be removed in some cases.

The idea is that A//C returns true if A is true, and true*1 throws an error, which is caught by try.

This can be shortened further by using EXP ?, which should be a shorthand for try EXP, though it seems like it does a lot more stuff, for example causing all subsequent filters to not throw errors. In this case (1?*(A//C)//B) works, but you might need to do some trial and error in your specific situation.
Note that ?// is its own operator since 1.6, so you need to add a space there.


Practical example: Fibonacci

A recursive filter using a conditional expression comes in at 41 bytes:

def f:if.<2then. else[.-1,.-2|f]|add end;

Applying the general approach gets this to 39 bytes:

def f:try(.<2//([.-1,.-2|f]|add))*1//.;

In this case add can be used to throw an error in the truthy case, 35 bytes:

def f:try(.<2//[.-1,.-2|f]|add)//.;

Using ? instead of try for 33 bytes:

def f:(.<2//[.-1,.-2|f]|add?)//.;

1 If you have a practical example where this is not the case, I'd like to see it.

2 Falsy values are null, false and empty, everything else is truthy.

\$\endgroup\$
8
  • \$\begingroup\$ Looking at this answer by Lynn, it might be possible to do ((A//C)*1? //B) instead. Will update this answer later \$\endgroup\$
    – ovs
    Sep 7 at 15:21
  • 2
    \$\begingroup\$ Best tip so far! You can even do (1?*(A//C)//B). \$\endgroup\$ Sep 7 at 16:24
  • \$\begingroup\$ @MichaelChatiskatzi that's nice. Do you know why this works? All I can find in the manual and github wiki is that ? is a postfix operator. (Even ((1?)*(A//C)//B) works) \$\endgroup\$
    – ovs
    Sep 7 at 16:53
  • \$\begingroup\$ I am really not sure how this works. I believe it is definitly a postfix operator but in these cases on 1 and not the whole expression. And as long as 1? is not used it won't be evaluated? That would be my best guess. \$\endgroup\$ Sep 7 at 17:11
  • 1
    \$\begingroup\$ @MichaelChatiskatzi It seems like ? works for the whole line and breaks // in later filters: (1?)|0|././/1. That's some strange behaviour \$\endgroup\$
    – ovs
    Sep 7 at 17:19
5
\$\begingroup\$

Consult Secret Reference

Spoiler: github.com/stedolan/jq/wiki/

JQ has a tutorial & manual on the official site, both of which I highly recommend!

However, on the github repo, there's an extensive wiki on the language with loads more info sure to help your golfing.

It includes:

  • FAQ section with:
    • Unofficial tutorials
    • Editor bindings, including vim & emacs
    • Language bindings, including Python & Java
    • Interactive playgrounds for JQ
  • Sections for:
    • Processing invalid json
    • Modules
    • Cookbook
    • Implementation details for numbers, regex and other built-ins
  • Other goodies
    • Lazy evaluation, generators and backtracking
    • Implement finite state machines in JQ?!
\$\endgroup\$
4
\$\begingroup\$

Sugary JSON

Overview

Instead of {foo:.foo, bar:.bar}, use {foo, bar}.

In both case, we're creating a new JSON object with the key foo mapping to the value of foo in the input.

Example

CODE:  {a, c}

IN:    {"a": 1, "b": 2, "c": 3, "d": 4}
OUT:   {"a": 1, "c": 3}

Try it online

\$\endgroup\$
3
\$\begingroup\$

Avoid split

When used on a string, the / operator behaves exactly like split, but is shorter by at least 3 bytes - and sometimes by as much as 7, such as when going from now|todate|split(":") to now|todate/":"

\$\endgroup\$
2
\$\begingroup\$

Avoid maps

Ungolfed: [1,2,3]|map(.+1)

Golfed: 1,2,3|.+1

Will add more here!

\$\endgroup\$
2
\$\begingroup\$

Pipe + Dot = Smell

They're sometimes needed, but this is often a code (golf) smell. You should be wary of any pipes followed by an expression containing a dot.

Or could it be possible to rewrite every jq program with a period into one without?

Examples

[99|range(.)] -> [range(99)]

\$\endgroup\$
1
\$\begingroup\$

If the coding challenge calls for dealing with individual characters in the input using explode can be handy. It breaks a string into an array of codepoints, allowing you pipe the results to a loop to process each character.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.