Split a String without removing the delimiter in S

2020-02-14 10:38发布

This might be a duplicate. I couldn't find the answer in Swift, so I am not sure.

componentsSeparatedByCharactersInSet removes the delimiter. If you separate by only one possible character it is easy to add it back. But what when you have a set?

Is there another method to split?

标签: swift
2条回答
够拽才男人
2楼-- · 2020-02-14 10:57

Swift 3 and 4 Versions

extension Collection {
    func splitAt(isSplit: (Iterator.Element) throws -> Bool) rethrows -> [SubSequence] {
        var p = self.startIndex
        var result:[SubSequence] = try self.indices.flatMap {
            i in
            guard try isSplit(self[i]) else {
                return nil
            }
            defer {
                p = self.index(after: i)
            }
            return self[p...i]
        }
        if p != self.endIndex {
            result.append(suffix(from: p))
        }
        return result
    }
}

Thanks to Oisdk for getting me thinking.

查看更多
冷血范
3楼-- · 2020-02-14 10:59

This method works on CollectionTypes, rather than Strings, but it should be easy enough to adapt:

extension CollectionType {
  func splitAt(@noescape isSplit: Generator.Element throws -> Bool) rethrows ->  [SubSequence] {
    var p = startIndex
    return try indices
      .filter { i in try isSplit(self[i]) }
      .map { i in
        defer { p = i }
        return self[p..<i]
      } + [suffixFrom(p)]
  }
}

extension CollectionType where Generator.Element : Equatable {
  func splitAt(splitter: Generator.Element) -> [SubSequence] {
    return splitAt { el in el == splitter }
  }
}

You could use it like this:

let sentence = "Hello, my name is oisdk. This should split: but only at punctuation!"

let puncSet = Set("!.,:".characters)

sentence
  .characters
  .splitAt(puncSet.contains)
  .map(String.init)

// ["Hello", ", my name is oisdk", ". This should split", ": but only at punctuation", "!"]

Or, this version, which uses a for-loop, and splits after the delimiter:

extension CollectionType {
  func splitAt(@noescape isSplit: Generator.Element throws -> Bool) rethrows ->  [SubSequence] {
    var p = startIndex
    var result: [SubSequence] = []
    for i in indices where try isSplit(self[i]) {
      result.append(self[p...i])
      p = i.successor()
    }
    if p != endIndex { result.append(suffixFrom(p)) }
    return result
  }
}


extension CollectionType where Generator.Element : Equatable {
  func splitAt(splitter: Generator.Element) -> [SubSequence] {
    return splitAt { el in el == splitter }
  }
}

let sentence = "Hello, my name is oisdk. This should split: but only at punctuation!"

let puncSet = Set("!.,:".characters)

sentence
  .characters
  .splitAt(puncSet.contains)
  .map(String.init)

// ["Hello,", " my name is oisdk.", " This should split:", " but only at punctuation!"]

Or, if you wanted to get the most Swift features into one function (defer, throws, a Protocol extension, an evil flatMap, guard, and Optionals):

extension CollectionType {
  func splitAt(@noescape isSplit: Generator.Element throws -> Bool) rethrows -> [SubSequence] {
    var p = startIndex
    var result: [SubSequence] = try indices.flatMap { i in
      guard try isSplit(self[i]) else { return nil }
      defer { p = i.successor() }
      return self[p...i]
    }
    if p != endIndex { result.append(suffixFrom(p)) }
    return result
  }
}
查看更多
登录 后发表回答