2

I would like to know if there is an efficient way of splitting a string into multiple strings based on delimiters that are also strings. Eg. updateStr = "23+45 = 56 0" , delimiters = ["+"," ","="]

Result = [23,45,56,0]

I tried the following code in swift:

for i in 0..<delimiter.count {
    let res = updateStr.components(separatedBy: delimiter[i])
    updateStr = res.joined(separator: "unique%")
 }
splitTxt = updateStr.components(separatedBy: "unique%")

This works, but as the delimiters will be received dynamically I want a better approach. Are there any efficient ways to avoid multiple loops to solve this? An algorithm with more efficient solution that doesn't involve swift instance methods would also be appreciated. Thanks for the answers but

To be clearer, I don't just want characters but strings as delimiters:

Eg2. updateStr = "I like playing with friends" , delimiters = ["li"," "la","ie"]

Result = ["I ","ke p","ying with fr","nds"]

3 Answers 3

5

The efficient way to do this sort of thing is with a Set:

let equation = "23+45 = 56 0"
let delimiters : [Character] = ["+"," ","="]
let setOfSeparators = Set(delimiters)
let result = equation.split {setOfSeparators.contains($0)}
print(result)

That's efficient because contains on a Set is extremely fast, so that cost is negligible and we are looping implicitly through the original string just once.

On the other hand, you could take advantage of the Cocoa CharacterSet class. For that, I would say:

let equation = "23+45 = 56 0"
let delimiters = ["+"," ","="]
let characterSet = CharacterSet(charactersIn: delimiters.joined())
let result = equation.components(separatedBy: characterSet).filter {!$0.isEmpty}
print(result)

Another fun way is to use a Scanner (these are underutilized in my opinion):

let equation = "23+45 = 56 0"
let delimiters = ["+"," ","="]
let characterSet = CharacterSet(charactersIn: delimiters.joined())
let scanner = Scanner(string: equation)
var result = [String]()
while let word = scanner.scanUpToCharacters(from: characterSet) {
    result.append(word)
    scanner.scanCharacters(from: characterSet)
}
print(result)
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer but as mentioned I want it for strings also.I have edited the question to include example for the same
What a beautiful answer
Well that doesn't change the first part of my answer, does it?
2

One of the components(separatedBy:) overloads will handle this automatically using a CharacterSet:

let delimiters = ["+"," ","="].compactMap(UnicodeScalar.init)
let splitTxt = updateStr.components(separatedBy: CharacterSet(delimiters))

8 Comments

Sorry, didn't mean to come in with the same thing.
@matt yours probably actually worked on the first try :D (I had to mess with it a few times to get it correct)
I added use of Scanner to mine so it doesn't look so much like yours. :)
Always going the extra mile.
@LeoDabus Yeah, it needs the same .filter {!$0.isEmpty} at the end that Matt added to his answer.
|
0

NSRegularExpression provides the facility to split on general regular expressions, so this would enable splitting on a finite set of string delimiters using a delim1|delim2|delim3 regex. The following split operation does this job:

static func stringSubrange(str : String, st : Int, en : Int) -> String
{ var result : [Character] = [Character]()
  var count : Int = 0

  for index in str.indices
  { let c : Character = str[index]
    count = count + 1
    if count >= st && count <= en
    { result.append(c) }
    else if count > en
    { return String(result) }
  }
  return String(result)
}


static func split(str: String, pattern: String) -> [String]
{ let rge = NSRange(location: 0, length: str.utf16.count)
  let regexp = try! NSRegularExpression(pattern: pattern)
  let pred = regexp.matches(in: str, options: [], range: rge)
  var result : [String] = [String]()
  var prev : Int = 1; 

  for p in pred
  { let range = p.range
    let splitString = Ocl.stringSubrange(str: str, st: prev, en: range.location)
    prev = range.location + range.length + 1
    if splitString.count > 0
    { result.append(splitString) }
  }

  if prev < str.count
  { result.append(Ocl.stringSubrange(str: str, st: prev, en: str.count)) } 
  return result
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.