Vectorized sprintf?

In R I can easily generate vectors of formatted strings like this:

> sprintf("a%.2d", 1:10)
 [1] "a01" "a02" "a03" "a04" "a05" "a06" "a07" "a08" "a09" "a10"

What is the closest in julia?

As @sprintf is a macro I cannot do

using Printf
@sprintf.("a%.2d", 1:10)
ERROR: syntax: invalid macro usage "@(sprintf.(("a%.2d", 1:10)))"

If I give up the formatting, I can do:

julia> string.("a", 1:10)
10-element Vector{String}:
 "a1"
 "a2"
 "a3"
 "a4"
 "a5"
 "a6"
 "a7"
 "a8"
 "a9"
 "a10"

The solutions I have found are map or list comprehension:

using Printf
map( x-> @sprintf("a%.2d", x), 1:10)
10-element Vector{String}:
 "a01"
 "a02"
 "a03"
 "a04"
 "a05"
 "a06"
 "a07"
 "a08"
 "a09"
 "a10"
[@sprintf("a%.2d", x) for x in 1:10];

but both are longer than broadcasting had @sprintf been a function.

What is the idiomatic solution?

You can try Printf.Format, see example here., or use a helper function as here.

Thank you @rafael.guerra .

They way I understand your comment, the suggestion is something like this:

Printf.format.(Ref(Printf.Format("a%.2d")),1:10)
10-element Vector{String}:
 "a01"
 "a02"
 "a03"
 "a04"
 "a05"
 "a06"
 "a07"
 "a08"
 "a09"
 "a10"

Also a bit long for my taste.

There’s a string macro which is a bit tidier. I wonder why Format objects aren’t made scalar under broadcasting, since they aren’t iterable / indexable. That’s a small change, a larger one would be to make them callable:

julia> using Printf: format, @format_str, Format

julia> Broadcast.broadcastable(f::Format) = Ref(f)

julia> format.(format"a%.2d", 1:10)
10-element Vector{String}:
 "a01"
[...]

julia> (f::Format)(x) = format(f, x)

julia> format"a%.2d".(1:10)
10-element Vector{String}:
 "a01"
[...]
7 Likes

Thanks @mcabbott

This is a lot closer to what I would expect from julia - in particular the last one. But it is annoying that @format_str is not exported and it does not work “out of the box”.

Would be an easy PR if anyone wants to take it on. Maybe file an issue and mark it with the “Good first issue” tag.

3 Likes

It’s not the closest to your example, but i guess most idiomatic in julia.

julia> "a" .* lpad.(1:10, 2, '0')
10-element Vector{String}:
 "a01"
 "a02"
 "a03"
 "a04"
 "a05"
 "a06"
 "a07"
 "a08"
 "a09"
 "a10"
1 Like

Thanks @rmsmsgood I totally forgot about lpad :smile: