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

pad function #2033

Open
elarrarte-d opened this issue Dec 19, 2019 · 12 comments
Open

pad function #2033

elarrarte-d opened this issue Dec 19, 2019 · 12 comments

Comments

@elarrarte-d
Copy link

elarrarte-d commented Dec 19, 2019

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!

@davidfetter
Copy link
Contributor

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

@elarrarte-d
Copy link
Author

Any updates about this?
Regards!

@davidfetter
Copy link
Contributor

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;

@itchyny
Copy link
Contributor

itchyny commented Feb 14, 2020

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.

@pkoppstein
Copy link
Contributor

pkoppstein commented Feb 14, 2020

@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] + .;

@itchyny
Copy link
Contributor

itchyny commented Feb 14, 2020

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

@itchyny
Copy link
Contributor

itchyny commented Feb 14, 2020

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

@davidfetter
Copy link
Contributor

Adjusted per comments, and thanks for looking at this!

@itchyny
Copy link
Contributor

itchyny commented Feb 14, 2020

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.

@elarrarte-d
Copy link
Author

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
Copy link
Author

elarrarte-d commented Mar 3, 2020

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

@krishnoid
Copy link

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.

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

5 participants