Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 451
Rick Avatar asked Sep 08 '15 18:09

Rick


4 Answers

One can also use the following approach to split a string with multiple delimiters in case keys are single characters:

//swift 4+
let stringData = "K01L02M03"
let res = stringData.components(separatedBy: CharacterSet(charactersIn: "KLM"))

//older swift syntax
let res = stringData.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "KLM"));

res will contain ["01", "02", "03"]

If anyone knows any kind of special syntax to extend the approach to multiple characters per key you are welcome to suggest and to improve this answer

like image 141
vir us Avatar answered Oct 21 '22 19:10

vir us


Swift 4.2 update to @vir us's answer:

let string = "dots.and-hyphens"
let array = string.components(separatedBy: CharacterSet(charactersIn: ".-"))
like image 41
CartoonChess Avatar answered Oct 21 '22 18:10

CartoonChess


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] }
  }
}
like image 39
oisdk Avatar answered Oct 21 '22 19:10

oisdk


Swift 5:

extension String {
    func components<T>(separatedBy separators: [T]) -> [String] where T : StringProtocol {
        var result = [self]
        for separator in separators {
            result = result
                .map { $0.components(separatedBy: separator)}
                .flatMap { $0 }
        }
        return result
    }
}

It's for the sack of nice and neat code, don't use it if you need something efficiently

like image 2
meomeomeo Avatar answered Oct 21 '22 17:10

meomeomeo