18

I have a string that has words and spaces, "2h 3m 1s". I want to extract 2h out of it; so get everything before first space.

var date = "1,340d 1h 15m 52s"  // I want to extract "1,340d".

What is the best practice of doing it? What substring function is the best approach?

senty
  • 12,385
  • 28
  • 130
  • 260

6 Answers6

36

If your string is heavy, componentsSeparatedByString() tends to be faster.

Swift 2:

var date = "1,340d 1h 15m 52s"
if let first = date.componentsSeparatedByString(" ").first {
    // Do something with the first component.
}

Swift 3/4/5:

if let first = date.components(separatedBy: " ").first {
    // Do something with the first component.
}
Laffen
  • 2,753
  • 17
  • 29
10

In this context, neither the .componentsSeparatedByString(" ") method nor the characters.split(" ") method is the "best approach". Both these methods will traverse the full String object, and give a String array as an result. Only after the array has been computed do we extract the first entry of this array. If we're treating a huge string, this is quite unnecessary in this context, and will result in an unnecessary overhead.

Instead, for a huge string, the following method is to prefer:

let firstDateEntryFast = date.substringToIndex((date.rangeOfString(" ")?.first)!)

This will look for the index of the first occurrence of " ", and thereafter return the substring from start of original string up only to the first occurrence. I.e., it will never investigate or make use of the original (in this context: assumed large) string beyond they point of the first " " occurrence.

You should note, however, that due to the force unwrap (operator (!)), this will crash at runtime if the string does not contain any instance of " ". So to stay safe, and follow the optional convention of Swift, use it in an if let clause:

if let myRange = date.rangeOfString(" ") {
    let firstDateEntryFast = date.substringToIndex(myRange.first!)
        // at this point, you know myRange is non-empty, and hence, .first can be
        // force-unwrapped
}
else {
    let firstDateEntryFast = date
        // no " " separation, unexpected? -> action
}

As in my first version of the answer, split can be used as an alternative (comparable with componentsSeparatedByString):

var date = "1,340d 1h 15m 52s"
let dateAsArray = date.characters.split(" ").map{ String($0) }
let firstDateEntry = dateAsArray[0]

Alternatively, skip storing them all in an array and directly get the first entry

var date = "1,340d 1h 15m 52s"
let firstDateEntryDirectly = String(date.characters.split(" ")[0])
dfrib
  • 70,367
  • 12
  • 127
  • 192
5

Swift 3 version

var str = "Hello, playground one two three"
let firstWord = str.components(separatedBy: " ").first
Pavle Mijatovic
  • 773
  • 10
  • 6
2

Use below code for your requirement

var date = "1,340d 1h 15m 52s"
print((date.componentsSeparatedByString(" ") as NSArray).objectAtIndex(0));
Narayana Rao Routhu
  • 6,303
  • 27
  • 42
0

Swift 5

var name = ""
if let optionalName = someOptionalString as? String {
    name = optionalName.contains(" ") ? (name.components(separatedBy: " ").first ?? "") : optionalName
}

First, we make sure the string is not nil. Then, we use the ternary operator to assign it accordingly based on it it contains any spaces or not.

grantespo
  • 2,233
  • 2
  • 24
  • 62
0

Have you tried Scanner? It's pretty fast and powerful.

var date = "1,340d 1h 15m 52s"  // I want to extract "1,340d".
let scanner = Scanner(string: date)
if let first = scanner.scanUpToCharacters(from: .whitespaces) {
    print("\(first)") // Prints "1,340d"
}

https://developer.apple.com/documentation/foundation/scanner

spstanley
  • 940
  • 7
  • 14