How to cut last characters of string and save it as variable in swift?

Viewed 158

I have a string of test@me

now I want to create a code that let me cut where the @ is and save it as a variable, so the expected result is string1 = test string2 = @me

a code will be something like this

func test(string: String) {
    var mString = string
    var cuttedString = mString.cutFrom("@")
    
    print(mString)
    print(cuttedString)
}

test(string: "test@me")

result:

test
@me
5 Answers

Use suffix(from:) in combination with firstIndex(of:)

let string = "test@me"

let last = string.suffix(from: string.firstIndex(of: "@") ?? string.startIndex)

Note that this returns the full string if "@" is not found, you could instead return an empty string by replacing string.startIndex with string.endIndex


To split the string in two parts, ie "test" and "@me"

var first: String = ""
var last: String = ""
if let index = string.firstIndex(of: "@") {
    last = String(string.suffix(from: index))
    first = String(string.prefix(upTo: index))
}

print(first, last)

Here is an extension of String which performs the function you desire. It takes a String and mutates the calling String by removing everything from that part on, and it returns the part that was removed.

import Foundation

extension String {
    mutating func cut(from string: String) -> String {
        if let range = self.range(of: string) {
            let cutPart = String(self[range.lowerBound...])
            self.removeSubrange(range.lowerBound...)
            return cutPart
        }
        else {
            return ""
        }
    }
}

func test(string: String) {
    var mString = string
    let cutString = mString.cut(from: "@")
    
    print(mString)
    print(cutString)
}

test(string: "test@me")
test
@me

A Generic Implementation

Here is a generic implementation suggested by @LeoDabus in the comments:

extension StringProtocol where Self: RangeReplaceableCollection {
    @discardableResult mutating func removeSubrange<S: StringProtocol>(from string: S) -> SubSequence {
        guard let range = range(of: string) else { return "" }
        defer { removeSubrange(range.lowerBound...) }
        return self[range.lowerBound...]
    }
}

It nicely demonstrates:

  1. Extending a protocol instead of String to allow the function to be used with other types such as String.Subsequence.
  2. The use of @discardableResult which allows the function to be called to shorten the String without using the substring that is returned.
  3. Using a guard let statement to unwrap the optional return from range(of:) and provide an early exit if the range is nil.
  4. The use of defer to delay the removal of the substring until after the substring has been returned which avoids the use a local variable.

You can use String subscript for this:

func test(string: String) {
    guard let atIndex = string.firstIndex(of: "@") else { return }
    
    let mString = string[string.startIndex...string.index(before: atIndex)]
    let cuttedString = string[atIndex..<string.endIndex]
    
    print(mString)
    print(cuttedString)
}
func test(string: String) {
    let mString = string.prefix { $0 != "@" }
    let cuttedString = string.suffix(from: string.firstIndex(of: "@") ?? string.startIndex)
    
    print(mString)
    print(cuttedString)
}

test(string: "test@me")
// prints test
//        @me

But remember that mString and cuttedString are not String but Substring, so take care of correct usage.

There are various ways to do this.

You could use components(separatedBy:) to break the string into pieces, and then add an @ back to the last one:

extension String {
    func cutFrom(_ divider: Character) -> String {
        let array = components(separatedBy: String(divider))
        //For an Optional, `map()` returns nil if the optional is empty, 
        //Or the result of applying the closure to the unwrapped contents if not
        return array.last.map {String(divider) + $0} ?? ""
    }
}

Alternately, you could use firstIndex to find the index of the first divider character in the string, and then return a substring from that index to the end:

extension String {
    func cutFrom(_ divider: Character) -> String {
        //For an Optional, `map()` returns nil if the optional is empty, 
        //Or the result of applying the closure to the unwrapped contents if not
        return firstIndex(of: divider).map { String(self[$0..<endIndex])} ?? ""
    }
}

It might be cleaner to have both versions of the function return Optionals, and return NIL if the divider character can't be found. You also might want to adjust the logic to deal with strings where the divider character occurs more than once.

Related