Reputation: 3751
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
Reputation: 4116
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)
}
}
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
}
}
}
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.
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!
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.
removingWhitespacesMap
18 seconds, 40,453 kB
removingWhitespacesRegex
10.1 seconds, 1,864,835 kB (!!!)
removingWhitespacesMap
21.5 seconds, 41,180 kB
removingWhitespacesRegex
0.45 seconds (!!!), 288,550 kB
Upvotes: 0
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
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
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
Reputation: 5792
For Swift 5:
" spaces here ".replacingOccurrences(of: " ", with: "")
returns:
"spaceshere"
Upvotes: 24
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
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
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
Reputation: 1949
This should work as of Swift 2.2:
let replaced = String(aString.characters.filter {$0 != " "})
Upvotes: 7
Reputation: 377
try this one:
let strArray0 = strArray1.map { $0.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) }
Hope this helps
Upvotes: 2
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
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