Search code examples
iosstringswiftsplit

Splitting a string in swift using multiple delimiters


I am trying to split (or explode) a string in Swift (1.2) using multiple delimiters, or seperators as Apple calls them.

My string looks like this:

KEY1=subKey1=value&subkey2=valueKEY2=subkey1=value&subkey2=valueKEY3=subKey1=value&subkey3=value

I have formatted it for easy reading:

KEY1=subKey1=value&subkey2=value
KEY2=subkey1=value&subkey2=value
KEY3=subKey1=value&subkey3=value

The uppercase "KEY" are predefined names.
I was trying to do this using:

var splittedString = string.componentsSeparatedByString("KEY1")

But as you can see, I can only do this with one KEY as the separator, so I am looking for something like this:

var splittedString = string.componentsSeperatedByStrings(["KEY1", "KEY2", "KEY3"])

So the result would be:

[
  "KEY1" => "subKey1=value&subkey2=value",
  "KEY2" => "subkey1=value&subkey2=value",
  "KEY3" => "subkey1=value&subkey2=value"
]

Is there anything built into Swift 1.2 that I can use? Or is there some kind of extension/library that can do this easily?

Thanks for your time, and have a great day!


Solution

  • This isn't very efficient, but it should do the job:

    import Foundation
    
    extension String {
      func componentsSeperatedByStrings(ss: [String]) -> [String] {
        let inds = ss.flatMap { s in
          self.rangeOfString(s).map { r in [r.startIndex, r.endIndex] } ?? []
        }
        let ended = [startIndex] + inds + [endIndex]
        let chunks = stride(from: 0, to: ended.count, by: 2)
        let bounds = map(chunks) { i in (ended[i], ended[i+1]) }
        return bounds
          .map { (s, e) in self[s..<e] }
          .filter { sl in !sl.isEmpty }
      }
    }
    
    
    
    "KEY1=subKey1=value&subkey2=valueKEY2=subkey1=value&subkey2=valueKEY3=subKey1=value&subkey3=value".componentsSeperatedByStrings(["KEY1", "KEY2", "KEY3"])
    
    // ["=subKey1=value&subkey2=value", "=subkey1=value&subkey2=value", "=subKey1=value&subkey3=value"]
    

    Or, if you wanted it in dictionary form:

    import Foundation
    
    extension String {
      func componentsSeperatedByStrings(ss: [String]) -> [String:String] {
        let maybeRanges = ss.map { s in self.rangeOfString(s) }
        let inds   = maybeRanges.flatMap { $0.map { r in [r.startIndex, r.endIndex] } ?? [] }
        let ended  = [startIndex] + inds + [endIndex]
        let chunks = stride(from: 0, to: ended.count, by: 2)
        let bounds = map(chunks) { i in (ended[i], ended[i+1]) }
        let values = bounds
          .map { (s, e) in self[s..<e] }
          .filter { sl in !sl.isEmpty }
        let keys = filter(zip(maybeRanges, ss)) { (r, _) in r != nil }
        var result: [String:String] = [:]
        for ((_, k), v) in zip(keys, values) { result[k] = v }
        return result
      }
    }
    
    
    "KEY1=subKey1=value&subkey2=valueKEY2=subkey1=value&subkey2=valueKEY3=subKey1=value&subkey3=value".componentsSeperatedByStrings(["KEY1", "KEY2", "KEY3"])
    
    // ["KEY3": "=subKey1=value&subkey3=value", "KEY2": "=subkey1=value&subkey2=value", "KEY1": "=subKey1=value&subkey2=value"]
    

    For Swift 2:

    import Foundation
    
    extension String {
      func componentsSeperatedByStrings(ss: [String]) -> [String] {
        let unshifted = ss
          .flatMap { s in rangeOfString(s) }
          .flatMap { r in [r.startIndex, r.endIndex] }
        let inds  = [startIndex] + unshifted + [endIndex]
        return inds.startIndex
          .stride(to: inds.endIndex, by: 2)
          .map { i in (inds[i], inds[i+1]) }
          .flatMap { (s, e) in s == e ? nil : self[s..<e] }
      }
    }