jq icon indicating copy to clipboard operation
jq copied to clipboard

pad function

Open elarrarte-d opened this issue 5 years ago • 12 comments

Hi!

Can you consider to add a builtin pad(n) function to jq?

Something like this: def pad(n): if n==0 then (.) else "0" + (.) | pad(n - 1) end;

Congratulations for this great tool!

elarrarte-d avatar Dec 19 '19 02:12 elarrarte-d

Maybe there could be a left pad (lpad) as above and a corresponding right pad (rpad) function.

davidfetter avatar Jan 02 '20 22:01 davidfetter

Any updates about this? Regards!

elarrarte-d avatar Feb 13 '20 23:02 elarrarte-d

How about something along this line? It's not recursive, but it's likely pretty fast.

def lpad(string;len;fill):
  if len == 0 then string else (fill * len)[0:len] + string end;
def rpad(string;len;fill):                                                                                                                                                                                                                                             
  if len == 0 then string else string + (fill * len)[0:len] end;

davidfetter avatar Feb 14 '20 02:02 davidfetter

If you want to pad string to make the result have the expected length, def lpad(string; len; fill): fill * (len - (string|length)) + string; (or def lpad(len; fill): fill * (len - length) + .;) would be correct.

itchyny avatar Feb 14 '20 03:02 itchyny

@DavidFetter: those defs are not idiomatic. As in @itchyny's second example, it would be more idiomatic for the string to be presented as input to the filter. It would also be in keeping with jq to use tostring on the input. So, allowing for the possibility that (fill|length) > 1, one might consider:

def lpad($len; $fill): tostring | ($len - length) as $l | ($fill * $l)[:$l] + .;

pkoppstein avatar Feb 14 '20 03:02 pkoppstein

By the way what's the difference of ($str * $l)[:$l] and ($str * $l)?

itchyny avatar Feb 14 '20 03:02 itchyny

Ahhhh, sorry, ignore my comment! It is different when ($str | length) != 1 as pkoppstein already commented out. Sorry.

itchyny avatar Feb 14 '20 04:02 itchyny

Adjusted per comments, and thanks for looking at this!

davidfetter avatar Feb 14 '20 07:02 davidfetter

By the way it doesn't work for multi-width characters, if you really want to pad spaces to strings to make them fit to a fixed width in terminals (wrap text by a frame, for example). String length and displayed width are different things.

itchyny avatar Feb 14 '20 07:02 itchyny

OK! I 've seen a lot of options here jeje. Are we going to have a pad() function or similar in the next release of jq?

Thanks!

elarrarte-d avatar Mar 03 '20 03:03 elarrarte-d

Add some performance tests:

Testing 1000 times [def pad(n): if n==0 then (.) else "0" + (.) | pad(n - 1) end]

time for i in $(seq 1000); do echo '{"test":"1"}' | jq -r 'def pad(n): if n==0 then (.) else "0" + (.) | pad(n - 1) end; .test | pad(4)'; done
real	0m3,856s
user	0m2,775s
sys	0m0,750s

Testing 1000 times [def lpad(string;len;fill): if len == 0 then string else (fill * len)[0:len] + string end]:

time for i in $(seq 1000); do echo '{"test":"1"}' | jq -r 'def lpad(string;len;fill): if len == 0 then string else (fill * len)[0:len] + string end; lpad(.test;4;"0")'; done
real	0m4,218s
user	0m2,964s
sys	0m0,878s

elarrarte-d avatar Mar 03 '20 04:03 elarrarte-d

Could it also handle multi-character fill strings? Then the fill multiplier would be (total_length - string_length) / fill_length, rounded up. As-is, it multiplies a multicharacter fill_string a lot more than it needs to before truncating it (e.g., if the fill-string is already long enough to handle the whole field width), but it still works just fine. Maybe it could accommodate for this if it were an internal function.

krishnoid avatar Apr 01 '22 23:04 krishnoid