30

Until Swift 2 I used this extension to remove multiple whitespaces:

func condenseWhitespace() -> String {
        let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).filter({!Swift.isEmpty($0)})
        return " ".join(components)
}

but with Swift 2 now I get the error

Cannot invoke 'isEmpty' with an argument list of type '(String)'

How could I now remove multiple spaces with Swift 2? Thnx!

headkit
  • 3,307
  • 4
  • 53
  • 99

6 Answers6

58

In Swift 2, join has become joinWithSeparator and you call it on the array.

In filter, isEmpty should be called on the current iteration item $0.

To replace whitespaces and newline characters with unique space characters as in your question:

extension String {
    func condenseWhitespace() -> String {
        let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
        return components.filter { !$0.isEmpty }.joinWithSeparator(" ")
    }
}

let result = "Hello  World.\nHello!".condenseWhitespace()  // "Hello World. Hello!"

Because your function does not take any parameter you could make it a property instead:

extension String {
    var condensedWhitespace: String {
        let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
        return components.filter { !$0.isEmpty }.joinWithSeparator(" ")
    }
}

let result = "Hello  World.\nHello!".condensedWhitespace  // "Hello World. Hello!"

In Swift 3 there's even more changes.

Function:

extension String {
    func condenseWhitespace() -> String {
        let components = self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines)
        return components.filter { !$0.isEmpty }.joined(separator: " ")
    }
}

let result = "Hello  World.\nHello!".condenseWhitespace()

Property:

extension String {
    var condensedWhitespace: String {
        let components = self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines)
        return components.filter { !$0.isEmpty }.joined(separator: " ")
    }
}

let result = "Hello  World.\nHello!".condensedWhitespace

In Swift 4.2 NSCharacterSet is now CharacterSet, and you can omit and use dot syntax:

extension String {
    func condenseWhitespace() -> String {
        let components = self.components(separatedBy: .whitespacesAndNewlines)
        return components.filter { !$0.isEmpty }.joined(separator: " ")
    }
}

let result = "Hello  World.\nHello!".condenseWhitespace()  // "Hello World. Hello!"
Paul Solt
  • 8,375
  • 5
  • 41
  • 46
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • be careful, if you want to make it work with whitespacesAndNewlines you need to manage new lines as new lines and not " " when you join. ** func condenseWhitespace() -> String { let components = self.components(separatedBy: .whitespaces) let tmpstr = components.filter { !$0.isEmpty }.joined(separator: " ") let components2 = tmpstr.components(separatedBy: .newlines) return components2.filter { !$0.isEmpty }.joined(separator: "\n") }** – Climbatize Sep 11 '19 at 14:04
  • 1
    The extension works great in Swift 5. Lifesaving code! Thank you :) – sugarakis Jan 06 '21 at 01:30
28

Split string to array and then join again in not memory efficient. Its Takes lot of memory. The best way in this case is to scan the given string and perform operations on that. Regular Expression is the advance way to scan a text. For the above conclusion the the solution is given below:

Swift 4.x

extension String {

    func removeExtraSpaces() -> String {
        return self.replacingOccurrences(of: "[\\s\n]+", with: " ", options: .regularExpression, range: nil)
    }

}

Usages

let startingString = "hello   world! \n\n I am   here!"
let processedString = startingString.removeExtraSpaces()
print(processedString)

Output:

processedString => "hello world! I am here!"

You can Do more according to your own requirements but thing I am pointing out here is to use regular expressions with string rather then create arrays which will consume lot of memory.

Gurjit Singh
  • 1,723
  • 21
  • 27
  • Thank you! This is better than creating unnecessary arrays. – ozd May 11 '20 at 08:29
  • @JeanNicolas For That, you can use .trimmingCharacters(in: .whitespacesAndNewlines) function by adding it to your extension. The Above solution is reducing more than 1 spaces or newlines to 1 space. I will really appreciate if you can make this functionality more responsible to trim the sting also. Thanks :-) – Gurjit Singh Aug 14 '20 at 05:38
12

Cleanest version. Documented, memory efficient, extremely easy to use.

extension String {

    /// Returns a condensed string, with no extra whitespaces and no new lines.
    var condensed: String {
        return replacingOccurrences(of: "[\\s\n]+", with: " ", options: .regularExpression, range: nil)
    }

    /// Returns a condensed string, with no whitespaces at all and no new lines.
    var extraCondensed: String {
        return replacingOccurrences(of: "[\\s\n]+", with: "", options: .regularExpression, range: nil)
    }

}

Usage:

let a = " Hello\n  I am  a string  ".condensed
let b = " Hello\n  I am  a string  ".extraCondensed

Output:

a: "Hello I am a string"

b: "HelloIamastring"

Lucio Botteri
  • 131
  • 1
  • 4
8

SWIFT 3: Cleaner version

extension String {
    var condensedWhitespace: String {
        let components = self.components(separatedBy: .whitespacesAndNewlines)
        return components.filter { !$0.isEmpty }.joined(separator: " ")
    }
}
Yaroslav Dukal
  • 3,894
  • 29
  • 36
1

Here is mine: How it's actually worked.

extension String {

    func removeExtraSpaces() -> String {
        var data  = ""
        var numberOfSpace = 0
        let items = self.getComponents(separatedBy: " ")
        for item in items{
            if item == " "{
                numberOfSpace = numberOfSpace + 1
            }else{
                numberOfSpace = 0
            }
            if numberOfSpace == 1 || numberOfSpace == 0 {
                data =  data + item
                //data.append(item)
            }
        }
        return data
    }
} 

Usages

 var message = "What  is the    purpose   of life?"
 message = message.removeExtraSpaces()
 print(message)

Output:

What is the purpose of life?

Nazmul Hasan
  • 10,130
  • 7
  • 50
  • 73
0
var str = "Hello  World.\nHello!"

if let regex = try? NSRegularExpression(pattern: "\\s+", options:NSRegularExpression.Options.caseInsensitive)
{
    str = regex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.count), withTemplate: " ")

}
Bhumika
  • 876
  • 7
  • 20
Manikandan
  • 131
  • 4