5

Looking for an (elegant) solution for splitting a string and keeping the separator as item(s) in the array

example 1:

"hello world" ["hello", " ", "world"] 

example 2:

" hello world" [" ", "hello", " ", "world"] 

thx.

2
  • 1
    What about this stackoverflow.com/q/32361412 ? Commented Feb 25, 2019 at 16:13
  • @MartinR thx for the link. I searched for solutions, and havent found this one. the solution(s) of that question are inspiring, yet don't work with separators longer than one char. example 2 seems to not work as well. Commented Feb 25, 2019 at 16:24

4 Answers 4

11

Suppose you are splitting the string by a separator called separator, you can do the following:

let result = yourString.components(separatedBy: separator) // first split .flatMap { [$0, separator] } // add the separator after each split .dropLast() // remove the last separator added .filter { $0 != "" } // remove empty strings 

For example:

let result = " Hello World ".components(separatedBy: " ").flatMap { [$0, " "] }.dropLast().filter { $0 != "" } print(result) // [" ", "Hello", " ", "World", " "] 
Sign up to request clarification or add additional context in comments.

2 Comments

@Sweeper I thought there was a version of components(separatedBy:) that had an optional parameter to exclude empty results. Have you seen anything like that?
I know you can do that in C# but I don't think that exists in Swift. @Alexander
0

For people who have a condition for their split, for example: splitting a camelCaseString based on uppercase condition:

extension Sequence { func splitIncludeDelimiter(whereSeparator shouldDelimit: (Element) throws -> Bool) rethrows -> [[Element]] { try self.reduce([[]]) { group, next in var group = group if try shouldDelimit(next) { group.append([next]) } else { group[group.lastIdx].append(next) } return group } } } 

For example:

"iAmCamelCase".splitIncludeDelimiter(whereSeparator: \.isUppercase) => ["i", "Am", "Camel", "Case"] 

(If you want the imp of isUppercase)

extension CharacterSet { static let uppercaseLetters = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZ") } extension Unicode.Scalar { var isUppercase: Bool { CharacterSet.uppercaseLetters.contains(self) } } 

1 Comment

Nice idea, but this doesn't compile. If you move the extension on RandomAccessCollection you do have an endIndex which can be used instead of lastIdx
0

Just for fun, the Swift Algorithms package contains an algorithm called Intersperse

After adding the package and

import Algorithms 

you can write

let string = "hello world" let separator = " " let result = Array(string .components(separatedBy: separator) .interspersed(with: separator)) print(result) 

Your second example is barely correct, the result of splitting " hello world" by space is

["", "hello", "world"] 

Comments

0
let sample = "a\nb\n\nc\n\n\nd\n\nddddd\n \n \n \n\n" let sep = "\n" let result = sample.components(separatedBy: sep).flatMap { $0 == "" ? [sep] : [$0, sep] }.dropLast() debugPrint(result) // ArraySlice(["a", "\n", "b", "\n", "\n", "c", "\n", "\n", "\n", "d", "\n", "\n", "ddddd", "\n", " ", "\n", " ", "\n", " ", "\n", "\n"]) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.