In Swift 2, the syntax for this has got more concise. The top-level split
function is now a method on CollectionType
(which String.CharacterView
conforms to).
There are two versions of the method, the first takes a closure as a predicate to indicate whether a given element should be treated as a separator, and the other simply takes an element to specify as the separator - this is likely what you want for 99% of string splits. They both have a couple of defaulted parameters (see the docs for details) which helps keep the method call nice and clear.
It's worth noting that split(_:)
returns a SubSequence
(basically a Slice
), so in most cases needs transforming back into an array of String
s which is generally more useful.
let str = "Today is so hot"
let words = str.characters.split(" ").map { String($0) } // or `.map(String.init)`
Explaining the shorthand initializer expression (map(String.init)
)
map
is a method that accepts a function as an argument. Most of the time, you probably just use a trailing closure (an unnamed function) as the argument to map
, e.g.:
func createString(from character: Character) -> String {
// ...
}
let strings = characters.map { createString(from: $0) }
But the following is more concise, and just as valid:
let strings = characters.map(createString(from:))
// or even:
let strings = characters.map(createString) // argument names inferred from context.
In the above example, createString(from:)
and createString
are both function expressions. map
expects a function as an argument, and a function expression can be passed as that argument. You can pass a function expression anywhere that accepts a function as an argument, which is why you can use sort
like this for a types that conform to Comparable
:
let sortedStrings = strings.sorted(by: <)
// `<` is just a function expression for a function that is essentially declared something like this:
func <(lhs: String, rhs: String) -> Bool {
// ...
}
String.init
is an initializer expression. It can be used in just the same way as a function expression – it is itself describing a function that takes a single Character
as an argument, and returns a value of type String
(though String.init
is overloaded and can be passed many different types of argument).
To help clarify what is going on, consider the following code, where each invocation of map
does exactly the same thing, but using an increasingly concise syntax:
// - Closure expression syntax used.
// - Explicit argument names & types.
// - Long-hand string initialization.
let strings = characters.map({ (character: CharacterView) -> String in
return String.init(character)
})
// - Trailing closure syntax used (parentheses omitted).
// - Closure argument and return types inferred from context (type names omitted).
// - Short-hand (standard, really) string initialization.
let strings = characters.map { character in
return String(character)
}
// - Implicit return since using single expression in the closure (`return` keyword omitted).
let strings = characters.map { character in String(character) }
// - Shorthand argument names (`in` keyword and argument names omitted).
let strings = characters.map { String($0) }
// - Initializer expression syntax used (curly braces omitted, argument implicitly passed to the initializer).
let strings = characters.map(String.init(_:))
// - Initializer expression argument names inferred from context.
let strings = characters.map(String.init)
String
andNSString
have/share the same underlaying code, possiblyCFString
. In such case forcomponentsSeparatedByString
there is really no difference. – Hinds