Kate Zz
Kate Zz

Reputation: 3751

How to remove whitespaces in strings in Swift?

This works

let replaced = String(map(aString.generate()) {
    $0 == " " ? "-" : $0 })

and this doesn't

let replaced = String(map(aString.generate()) {
    $0 == " " ? "" : $0 })

Why?

Upvotes: 16

Views: 30293

Answers (12)

Trev14
Trev14

Reputation: 4116

Swift 5 --- Best answer based on performance tests

I took a look at the two most common answers and did some performance tests. Both were similar in time performance when about half the characters were whitespace, but there was always big difference in memory performance.

Note: Take a look at additional performance data at the bottom - if you have a lot of large strings with very few whitespaces in them, the regex version (named removingWhitespacesRegex below) was much, much faster.

// Tested on 1,000 character string with 500 whitespace characters

extension String {

    // Best performance memory-wise
    var removingWhitespacesMap: String {
        String(self.compactMap { $0.isWhitespace ? nil : $0 })
    }

    // Far less efficient memory-wise
    var removingWhitespacesRegex: String {
        replacingOccurrences(of: "\\s", with: "", options: .regularExpression)
    }

}

Performance test details

I ran both methods 100,000 times on a 1,000 character string containing 500 whitespace characters (spaces and new lines). E.g.

func test_String_removingWhitespacesMap_performance() {
    measure(metrics: [XCTClockMetric(), XCTMemoryMetric()]) {
        for _ in 1...100000 {
            let _ = whitespaceString.removingWhitespacesMap
        }
    }
}

Time performance

The removingWhitespacesMap method took about 7.9 seconds to complete compared to removingWhitespacesRegex taking 7.6 seconds. Not surprising that the RegEx optimized method performed a little better with such huge input strings. I don't think this time difference is enough to matter unless you're working with millions of strings.

Memory performance

I looked at the "Memory Peak Physical" measurement since "Memory Physical" often comes back negative and I couldn't find anywhere what that is supposed to mean. The removingWhitespacesMap method used 41,301 kB memory peak physical, while removingWhitespacesRegex used 805,062 kB memory peak physical, about 20x more!

Additional Testing

This was interesting, you may want to take these results into account if you know your string is mostly whitespace or mostly non-whitespace. Specifically, if the string is large and mostly non-whitespace, you might want to use the regex version. It was much faster on a large scale.

1,000 character string with 999 spaces and 1 non-whitespace character at the end:

removingWhitespacesMap 18 seconds, 40,453 kB

removingWhitespacesRegex 10.1 seconds, 1,864,835 kB (!!!)

1,000 character string with 1 space at the end and 999 non-whitespace characters:

removingWhitespacesMap 21.5 seconds, 41,180 kB

removingWhitespacesRegex 0.45 seconds (!!!), 288,550 kB

Upvotes: 0

carlynorama
carlynorama

Reputation: 314

NOTE: juhan_h's answer on this question is a more succinct version if whitespace is really all you're interested in stripping out. https://stackoverflow.com/a/57425783/5946596

Swift 5.5

All the answers are great for removing spaces but sometimes I want to remove any whitespaces and newLines throwing in _ and - ... who knows.

The following extension on string lets me do that.

extension String {
    func removingCharacters(_ characters:CharacterSet) -> Self {
        Self(self.unicodeScalars.filter {
            !characters.contains($0)
        })
    }
    func removingCharacters(in string:String) -> Self {
        Self(self.unicodeScalars.filter {
            !CharacterSet(charactersIn:string).contains($0)
        })
    }
    
    func replacingCharacters(_ characters:CharacterSet, with newChar:Character) -> Self {
        String(self.compactMap( {
            CharacterSet(charactersIn: "\($0)").isSubset(of: characters)
             ? newChar : $0
        }))
    }
    
    func replacingCharacters(in string:String, with newChar:Character) -> Self {
        String(self.compactMap( {
            CharacterSet(charactersIn: "\($0)").isSubset(of: CharacterSet(charactersIn:string))
             ? newChar : $0
        }))
    }
}

usage:

print("hello \n my name\t is Joe".removingCharacters(.whitespacesAndNewlines))
print("hello \n my name\t is Joe".removingCharacters(in: " \t\n"))

print("ban annan anann ana".replacingCharacters(.whitespacesAndNewlines, with: "_"))
print("ban-annan anann ana".replacingCharacters(in: " -", with: "_"))

Obviously for just spaces the .replacingOccurrences(of: " ", with: "") is better.

I have not done a performance comparison to the

let toArray = aString.components(separatedBy: characterSet)
let backToString = toArray.joined(separator: "+") 

style done in Ramis's extension on this question:

https://stackoverflow.com/a/33120336/5946596

I'd be interested if someone does.

See also replacing emojis: https://stackoverflow.com/a/63416058/5946596

Upvotes: 0

iAnkit
iAnkit

Reputation: 1960

If you want to remove white space from string then just pass string with stringByReplacingOccurrencesOfString function like below,

let replacedString = string.replacingOccurrences(of: " ", with: "")

For Text Fields, you can apply directly object of UITextField,

let replacedString = textField.text!.replacingOccurrences(of: " ", with: "")

Upvotes: 8

Martin R
Martin R

Reputation: 539795

Enumerating a string gives a sequence of characters, so $0 inside the closure has the type Character. This compiles

{ $0 == " " ? "-" : $0 }

because "-" in this context is interpreted as a character literal and therefore of the same type as $0. But

{ $0 == " " ? "" : $0 }

does not compile because "" is not a character literal (and in the conditional expression a ? b : c the operands b and c must have the same type).

You can fix that by converting $0 to a string:

{ $0 == " " ? "" : String($0) }

but now the mapping returns an array of strings instead of an array of characters. So instead of the String() constructor you have to join the results:

let replaced = "".join(map(aString) { $0 == " " ? "" : String($0) })
// Swift 2 / Xcode 7:
let replaced = "".join(aString.characters.map({ $0 == " " ? "" : String($0) }))

(Note that calling generate() explicitly is not needed.)

Of course the same result would also be achieved with

// Before Swift 3.0
let replaced = aString.stringByReplacingOccurrencesOfString(" ", withString: "")

// After Swift 3.0
let replaced = aString.replacingOccurrences(of: " ", with: "")

Upvotes: 18

Shaked Sayag
Shaked Sayag

Reputation: 5792

For Swift 5:

" spaces here ".replacingOccurrences(of: " ", with: "")

returns:

"spaceshere"

Upvotes: 24

juhan_h
juhan_h

Reputation: 4011

None of the previous answers where Swifty enough for me, so I ended up with this using Swift 5:

let nonWhitespaceString = String(whitespaceString.compactMap({ $0.isWhitespace ? nil : $0 })

Upvotes: 4

5keeve
5keeve

Reputation: 186

If you want to remove all whitespaces anywhere in the String I did come up with this solution for Swift 3.0:

let number = "+000 000 000"
let nonWhiteCharacters = number.unicodeScalars.filter {
    false == NSCharacterSet.whitespacesAndNewlines.contains($0)
}.map(Character.init)
let whitespacelessNumber = String(nonWhiteCharacters)

or even better (you will need generic extension on Sequence):

extension Sequence {
    public func reduce<Result>(_ result: (Self) throws -> Result) rethrows -> Result {
        return try result(self)
    }
}

and then you can write:

let whitespacelessNumber = number.unicodeScalars.filter {
    false == NSCharacterSet.whitespacesAndNewlines.contains($0)
}.map(Character.init).reduce { String($0) }

where you can also replace NSCharacterSet.whitespacesAndNewlines for any of other character sets:

NSCharacterSet.controlCharacters
NSCharacterSet.whitespaces
NSCharacterSet.whitespacesAndNewlines
NSCharacterSet.decimalDigits
NSCharacterSet.letters
NSCharacterSet.lowercaseLetters
NSCharacterSet.uppercaseLetters
NSCharacterSet.nonBaseCharacters
NSCharacterSet.alphanumerics
NSCharacterSet.decomposables
NSCharacterSet.illegalCharacters
NSCharacterSet.punctuationCharacters
NSCharacterSet.capitalizedLetters
NSCharacterSet.symbols
NSCharacterSet.newline

Upvotes: 4

Dharmendra Chaudhary
Dharmendra Chaudhary

Reputation: 267

In Swift 3.0 DO as

func RemoveWhiteSpace(aString:String) -> String
    {
        let replaced = aString.trimmingCharacters(in: NSCharacterSet.whitespaces)
        return replaced
    }

And use like this

let nonWhiteSpaceStr = self.RemoveWhiteSpace(aString: "I have white Space        ")

Upvotes: 1

EFC
EFC

Reputation: 1949

This should work as of Swift 2.2:

let replaced = String(aString.characters.filter {$0 != " "})

Upvotes: 7

Daniel
Daniel

Reputation: 377

try this one:

let strArray0 = strArray1.map { $0.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) }

Hope this helps

Upvotes: 2

Henk-Martijn
Henk-Martijn

Reputation: 2024

If you want to delete whitespaces before and after a string, which is very useful in user input forms, you can use:

let replaced = aString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())

You can apply it directly on a textfield as well.

Upvotes: 5

Matteo Piombo
Matteo Piombo

Reputation: 6726

You are mapping thus the number of elements should be preserved. In the second case you remove elements. Your example will fail even in case you replace " " with --.

You might prefer using filter:

let replaced = String(filter(aString.generate()) { $0 != " "})

Upvotes: 3

Related Questions