BadmintonCat
BadmintonCat

Reputation: 9586

Splitting a string at several indices --> [String]?

I need to figure out a way to split strings at certain character sequences into an array of strings but the problem with my current implementation is that if there are two or more splits occurring in a string, it'll overwrite the former one.

Consider splitting the following string at st and ck ...

stockwell

With regexp match I get two matches with ranges but if I try to assemble these to an array afterwards in a loop, the second match overwrites the first...

stockwell: (0,2) --> st --> _st_ockwell
stockwell: (3,2) --> ck --> sto_ck_well

What would be optimal for this is a String extension method that can split a string into a [String] at several indices but I haven't been successful yet to come up with one.

Can somebody give me some help here?

Upvotes: 0

Views: 151

Answers (3)

user887210
user887210

Reputation:

Another way to do this, it depends on making sure that your separators don't overlap or contain each other.

import Foundation

let initial = "stockwell"
let firstSeparator = "st"
let secondSeparator = "ck"
var separated = [String]()
let firstSplit = initial.componentsSeparatedByString(firstSeparator)
for next in [firstSeparator].join(firstSplit.map{[$0]}) {
  let secondSplit = next.componentsSeparatedByString(secondSeparator)
  separated += [secondSeparator].join(secondSplit.map{[$0]})
}
separated // => ["", "st", "o", "ck", "well"]

It works by separating the string into an array using the first delimiter, inserting the delimiter into the array between elements, then doing the same to every element in the new array with the next delimiter.

Upvotes: 1

oisdk
oisdk

Reputation: 10091

How about this:

extension String {

  func multiSplit(inds: [String.Index]) -> [String] {

    if let loc = inds.last {

      return self[self.startIndex..<loc].multiSplit(Array(dropLast(inds))) + [self[loc..<self.endIndex]]

    } else {

      return [self]

    }
  }
}

let string = "abcdef"

let inds = [
  string.startIndex.successor().successor(),
  string.endIndex.predecessor().predecessor()
]

string.multiSplit(inds) // ["ab", "cd", "ef"]

The indices you give it have to be in order to work.

It's a recursive function - it takes the last index in the array of indices it's given (inds), and splits the string at that index. The first half of that split is recursively given to itself, with the last element of the array of indices removed. The "if let" fails if the index array is empty, so it just returns the string without any splits, but in an array of one.

In the example, It'll first split at the last index it's given, and get two strings: "abcd" and "ef". Then, the function calls itself with "abcd", and an array of indices without the last element (dropLast()). In this function, it'll split "abcd" into "ab" and "cd", and call itself again on "ab". But, since it passes an empty array to that function, the inds.last will fail, and it won't split anything, and just return ["ab"] (the line with return [self]). This goes up to the function above - which appends its split ("cd") onto it, and returns ["ab", "cd"]. This, finally, goes to the function above, which appends its split, and it returns the answer: ["ab, "cd", "ef"].

It's all just normal Swift, though, no imports or anything.

If you were dealing with indices out of order, you can just sort them:

let string = "abcdef"

let inds = [
  string.endIndex.predecessor().predecessor(),
  string.startIndex.successor().successor()
]

string.multiSplit(sorted(inds)) // ["ab", "cd", "ef"]

Also, if you want to remove empty strings from within the function:

extension String {

  func multiSplit(inds: [String.Index]) -> [String] {

    if let loc = inds.last {

    return (
      self[self.startIndex..<loc]
      .multiSplit(Array(dropLast(inds)))
      + [self[loc..<self.endIndex]]
      ).filter{!$0.isEmpty}

    } else {

      return [self]

    }
  }
}

Upvotes: 1

karthik manchala
karthik manchala

Reputation: 13640

You can use lookahead and lookbehind for matching the positions:

(?=st)|(?<=st)|(?=ck)|(?<=ck)

And replace with _

See DEMO

Upvotes: 1

Related Questions