From 86b07b2797b69f87fee845a93b9967bdcc99e7fc Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Sun, 9 Dec 2018 13:53:01 -0800 Subject: [PATCH] Allow passing a function to join Patterns such as `join(map(uppercase, xs))` can now be written more succinctly and efficiently as `join(uppercase, xs)`. --- NEWS.md | 1 + base/strings/io.jl | 26 +++++++++++++++++++------- test/strings/io.jl | 3 +++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2833e53cb3f43..134cccb1a097a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -95,6 +95,7 @@ Standard library changes `--project=@.` ([#29108]). * The `spawn` API is now more flexible and supports taking IOBuffer directly as a I/O stream, converting to a system pipe as needed ([#30278]). + * `join` now accepts a function argument ([#TODO]). #### Dates * New `DateTime(::Date, ::Time)` constructor ([#29754]). diff --git a/base/strings/io.jl b/base/strings/io.jl index 71767cefb52af..01b351be5e3e7 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -230,52 +230,64 @@ IOBuffer(s::SubString{String}) = IOBuffer(view(unsafe_wrap(Vector{UInt8}, s.stri # join is implemented using IO """ - join([io::IO,] strings, delim, [last]) + join([io::IO,] [f::Function,] strings, delim, [last]) Join an array of `strings` into a single string, inserting the given delimiter between adjacent strings. If `last` is given, it will be used instead of `delim` between the last two strings. If `io` is given, the result is written to `io` rather than returned as -as a `String`. +as a `String`. If `f` is given, the function is applied to each string before joining. + +!!! compat "Julia 1.1" + Providing a function argument requires Julia 1.1 or later. # Examples ```jldoctest julia> join(["apples", "bananas", "pineapples"], ", ", " and ") "apples, bananas and pineapples" + +julia> join(uppercasefirst, ["pears", "rhubarb"], " & ") +"Pears & Rhubarb" ``` `strings` can be any iterable over elements `x` which are convertible to strings via `print(io::IOBuffer, x)`. `strings` will be printed to `io`. """ -function join(io::IO, strings, delim, last) +function join(io::IO, f::Function, strings, delim, last) first = true local prev for str in strings if @isdefined prev first ? (first = false) : print(io, delim) - print(io, prev) + print(io, f(prev)) end prev = str end if @isdefined prev first || print(io, last) - print(io, prev) + print(io, f(prev)) end nothing end -function join(io::IO, strings, delim="") +function join(io::IO, f::Function, strings, delim="") # Specialization of the above code when delim==last, # which lets us emit (compile) less code first = true for str in strings first ? (first = false) : print(io, delim) - print(io, str) + print(io, f(str)) end end +join(io::IO, strings, delim="") = join(io, identity, strings, delim) +join(io::IO, strings, delim, last) = join(io, identity, strings, delim, last) join(strings) = sprint(join, strings) join(strings, delim) = sprint(join, strings, delim) join(strings, delim, last) = sprint(join, strings, delim, last) +join(f::Function, strings) = sprint((io, s)->join(io, f, s), strings) +join(f::Function, strings, delim) = sprint((io, s)->join(io, f, s, delim), strings) +join(f::Function, strings, delim, last) = sprint((io, s)->join(io, f, s, delim, last), strings) + ## string escaping & unescaping ## need_full_hex(c::Union{Nothing, AbstractChar}) = c !== nothing && isxdigit(c) diff --git a/test/strings/io.jl b/test/strings/io.jl index e0d3c2284d81a..b3b9d85e143d2 100644 --- a/test/strings/io.jl +++ b/test/strings/io.jl @@ -156,6 +156,9 @@ end @test join(()) == "" @test join((), ", ") == "" @test join((), ", ", ", and ") == "" + @test join(uppercase, ["a", "b", "c"]) == "ABC" + @test join(abs2, 1:4, " ") == "1 4 9 16" + @test join(x->round(x, digits=3), [π, π, π], ' ', "... ") == "3.142 3.142... 3.142" end # quotes + interpolation (issue #455)