Strip everything from an array of strings after certain character

Viewed 621

This rather simple julia issue has got me a little perplexed.

Let's say I have an array of strings of the form "xxx_xxx". Is there a function that will remove everything from "_" onward?

Example

names = ["last1_first1","last2_first2"]

Is there a simple function that will return

["last1","last2"]
4 Answers

You are looking for split:

julia> names  = ["last1_first1", "last2_first2"]
2-element Array{String,1}:
 "last1_first1"
 "last2_first2"

julia> split.(names, "_")
2-element Array{Array{SubString{String},1},1}:
 ["last1", "first1"]
 ["last2", "first2"]

Note that I am using the dot broadcasting syntax split.() here to apply the split function elementwise to the array of names.

For completeness one way of pulling out the last elements is:

julia> hcat(split.(names, "_")...)[1, :]
2-element Array{SubString{String},1}:
 "last1"
 "last2"

As Cameron points out in the comments, if it is (as in this example) the first element you're after, you can replace the hcat and splatting with:

julia> first.(split.(names, "_"))
2-element Array{SubString{String},1}:
 "last1"
 "last2"

This is slightly less flexible for those cases where you want the nth index. For those cases you can broadcast getindex and pass it the index of the element you're after:

julia> julia> getindex.(split.(names, "_"), 2)
2-element Array{SubString{String},1}:
 "first1"
 "first2"

Alternatively building on the regex idea in your own answer, you can simply do:

julia> replace.(names, r"_.*" => "")
2-element Array{String,1}:
 "last1"
 "last2"

If you need this to be fast you can write:

(s -> s[1:prevind(s, findfirst('_', s))]).(names)

or if you want to be even faster:

(s -> SubString(s, 1, prevind(s, findfirst('_', s)))).(names)

If you are interested in the performance of your code 6x - 10x faster is:

SubString.(names,1,findfirst.(==('_'),names).-1)

Some benchmarks:

julia> using Random, BenchmarkTools

julia> const names=string.(randstring.(repeat([5],1000)),"_",randstring.(repeat([5],1000)));

julia> @btime  replace.(names, r"_.*" => "");
  264.801 μs (5002 allocations: 257.97 KiB)

julia> @btime hcat(split.(names, "_")...)[1, :];
  160.000 μs (4012 allocations: 243.05 KiB)

julia> @btime SubString.(names,1,findfirst.(==('_'),names).-1);
  26.599 μs (1001 allocations: 39.19 KiB)

If your text contains non ASCII characters you should use prevind instead of .-1

I was just having some syntax issues

names[:] = map((x) -> replace(x,r"_.*",""),names[:])
Related