What is the most succinct way to remove the first character from a string in Swift?

StringSwift

String Problem Overview


I want to delete the first character from a string. So far, the most succinct thing I've come up with is:

display.text = display.text!.substringFromIndex(advance(display.text!.startIndex, 1))

I know we can't index into a string with an Int because of Unicode, but this solution seems awfully verbose. Is there another way that I'm overlooking?

String Solutions


Solution 1 - String

If you're using Swift 3, you can ignore the second section of this answer. Good news is, this is now actually succinct again! Just using String's new remove(at:) method.

var myString = "Hello, World"
myString.remove(at: myString.startIndex)

myString // "ello, World"

I like the global dropFirst() function for this.

let original = "Hello" // Hello
let sliced = dropFirst(original) // ello

It's short, clear, and works for anything that conforms to the Sliceable protocol.

If you're using Swift 2, this answer has changed. You can still use dropFirst, but not without dropping the first character from your strings characters property and then converting the result back to a String. dropFirst has also become a method, not a function.

let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello

Another alternative is to use the suffix function to splice the string's UTF16View. Of course, this has to be converted back to a String afterwards as well.

let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello

All this is to say that the solution I originally provided has turned out not to be the most succinct way of doing this in newer versions of Swift. I recommend falling back on @chris' solution using removeAtIndex() if you're looking for a short and intuitive solution.

var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)

original // ello

And as pointed out by @vacawama in the comments below, another option that doesn't modify the original String is to use substringFromIndex.

let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello

Or if you happen to be looking to drop a character off the beginning and end of the String, you can use substringWithRange. Just be sure to guard against the condition when startIndex + n > endIndex - m.

let original = "Hello" // Hello

let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)

let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell

The last line can also be written using subscript notation.

let substring = original[newStartIndex..<newEndIndex]

Solution 2 - String

Update for Swift 4

In Swift 4, String conforms to Collection again, so it is possible to use dropFirst and dropLast to trim the beginnings and ends of strings. The result is of type Substring, so you need to pass that to the String constructor to get back a String:

let str = "hello"
let result1 = String(str.dropFirst())    // "ello"
let result2 = String(str.dropLast())     // "hell"

dropFirst() and dropLast() also take an Int to specify the number of characters to drop:

let result3 = String(str.dropLast(3))    // "he"
let result4 = String(str.dropFirst(4))   // "o"

If you specify more characters to drop than are in the string, the result will be the empty string ("").

let result5 = String(str.dropFirst(10))  // ""

Update for Swift 3

If you just want to remove the first character and want to change the original string in place, then see @MickMacCallum's answer. If you want to create a new string in the process, use substring(from:). With an extension to String, you can hide the ugliness of substring(from:) and substring(to:) to create useful additions to trim the start and ends of a String:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return substring(from: index(startIndex, offsetBy: count))
    }
    
    func chopSuffix(_ count: Int = 1) -> String {
        return substring(to: index(endIndex, offsetBy: -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Like dropFirst and dropLast before them, these functions will crash if there aren't enough letters available in the String. The onus is on the caller to use them properly. This is a valid design decision. One could write them to return an optional which then would have to be unwrapped by the caller.


Swift 2.x

Alas in Swift 2, dropFirst and dropLast (the previous best solution) aren't as convenient as they were before. With an extension to String, you can hide the ugliness of substringFromIndex and substringToIndex:

extension String {
    func chopPrefix(count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }
    
    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Like dropFirst and dropLast before them, these functions will crash if there aren't enough letters available in the String. The onus is on the caller to use them properly. This is a valid design decision. One could write them to return an optional which then would have to be unwrapped by the caller.


In Swift 1.2, you'll need to call chopPrefix like this:

"hello".chopPrefix(count: 3)  // "lo"

or you can add an underscore _ to the function definitions to suppress the parameter name:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }
    
    func chopSuffix(_ count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

Solution 3 - String

Swift 2.2

> 'advance' is unavailable: call the 'advancedBy(n)' method on the index

    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }
    
    func chopSuffix(count: Int = 1) -> String {
        return self.substringFromIndex(self.endIndex.advancedBy(count))
    }

Swift 3.0

    func chopPrefix(_ count: Int = 1) -> String {
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
       return self.substring(to: self.characters.index(self.endIndex, offsetBy: -count))
    }

Swift 3.2

> A view of the string's contents as a collection of characters.

> @available(swift, deprecated: 3.2, message: "Please use String or Substring directly") > public var characters: String.CharacterView

func chopPrefix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(from: String.Index(encodedOffset: count))
    }
    return ""
}

func chopSuffix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(to: String.Index(encodedOffset: self.count - count))
    }
    return ""
}

Swift 4

extension String {

    func chopPrefix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexStartOfText = self.index(self.startIndex, offsetBy: count)
            return String(self[indexStartOfText...])
        }
        return ""
    }

    func chopSuffix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexEndOfText = self.index(self.endIndex, offsetBy: -count)
            return String(self[..<indexEndOfText])
        }
        return ""
    }
}

Solution 4 - String

Depends on what what you want the end result to be (mutating vs nonmutating).

As of Swift 4.1:

Mutating:

var str = "hello"
str.removeFirst() // changes str 

Nonmutating:

let str = "hello"
let strSlice = str.dropFirst() // makes a slice without the first letter
let str2 = String(strSlice)

Notes:

  • I put an extra step in the nonmutating example for clarity. Subjectively, combining the last two steps would be more succinct.
  • The naming of dropFirst seems a bit odd to me because if I am understanding the Swift API Design Guidelines correctly, dropFirst should really be something like droppingFirst because it is nonmutating. Just a thought :).

Solution 5 - String

In Swift 2, do this:

let cleanedString = String(theString.characters.dropFirst())

I recommend https://www.mikeash.com/pyblog/friday-qa-2015-11-06-why-is-swifts-string-api-so-hard.html to get an understanding of Swift strings.

Solution 6 - String

What about this?

s.removeAtIndex(s.startIndex)

This of course assumes that your string is mutable. It returns the character which has been removed, but alters the original string.

Solution 7 - String

The previous answers are pretty good, but as of today, I think this may be the most succinct way to remove the first character from a string in Swift 4:

var line: String = "This is a string..."
var char: Character? = nil

char = line.removeFirst()

print("char = \(char)")  // char = T
print("line = \(line)")  // line = his is a string ...

Solution 8 - String

I know of nothing more succinct out of the box, but you could easily implement prefix ++, e.g.,

public prefix func ++ <I: ForwardIndexType>(index: I) -> I {
    return advance(index, 1)
}

After which you can use it to your heart's content very succinctly:

str.substringFromIndex(++str.startIndex)

Solution 9 - String

In Swift 2 use this String extension:

extension String
{
    func substringFromIndex(index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substringFromIndex(self.startIndex.advancedBy(index))
    }
}

display.text = display.text!.substringFromIndex(1)

Solution 10 - String

> "en_US,fr_CA,es_US".chopSuffix(5).chopPrefix(5) // ",fr_CA,"

extension String {
    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(self.endIndex.advancedBy(-count))
    }
}

Solution 11 - String

To remove first character from string

let choppedString = String(txtField.text!.characters.dropFirst())

Solution 12 - String

Swift3

extension String {
	func chopPrefix(_ count: UInt = 1) -> String {
		return substring(from: characters.index(startIndex, offsetBy: Int(count)))
	}

	func chopSuffix(_ count: UInt = 1) -> String {
		return substring(to: characters.index(endIndex, offsetBy: -Int(count)))
	}
}

class StringChopTests: XCTestCase {
	func testPrefix() {
		XCTAssertEqual("original".chopPrefix(0), "original")
		XCTAssertEqual("Xfile".chopPrefix(), "file")
		XCTAssertEqual("filename.jpg".chopPrefix(4), "name.jpg")
	}

	func testSuffix() {
		XCTAssertEqual("original".chopSuffix(0), "original")
		XCTAssertEqual("fileX".chopSuffix(), "file")
		XCTAssertEqual("filename.jpg".chopSuffix(4), "filename")
	}
}

Solution 13 - String

Here is a Swift4 crash save version of the chopPrefix extension, leaving chopSuffix to the community ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSSteveView Question on Stackoverflow
Solution 1 - StringMick MacCallumView Answer on Stackoverflow
Solution 2 - StringvacawamaView Answer on Stackoverflow
Solution 3 - StringMohamed Jaleel NazirView Answer on Stackoverflow
Solution 4 - Stringjason zView Answer on Stackoverflow
Solution 5 - StringjrcView Answer on Stackoverflow
Solution 6 - StringchrisView Answer on Stackoverflow
Solution 7 - StringByteSlingerView Answer on Stackoverflow
Solution 8 - StringGregory HigleyView Answer on Stackoverflow
Solution 9 - StringChikabuZView Answer on Stackoverflow
Solution 10 - StringAndrewView Answer on Stackoverflow
Solution 11 - StringHardik ThakkarView Answer on Stackoverflow
Solution 12 - StringneoneyeView Answer on Stackoverflow
Solution 13 - StringiDocView Answer on Stackoverflow