I wasn't happy using Foundation for the String.trimmingCharacters() function, so I decided to roll my own more general one. I'd be interested to hear any thoughts.
public enum Trim {
case both, leading, trailing
}
extension BidirectionalCollection {
public typealias ElementEvaluation = (Element) throws -> Bool
public typealias ElementEvaluationMethod = (Element) throws -> () throws -> Bool
public typealias ElementProperty = KeyPath<Element, Bool>
public var lastIndex: Index { return index(before: endIndex) }
public func trimming(_ trimEnd: Trim = .both, where predicate: ElementEvaluation) rethrows -> SubSequence {
let leftIndex = (trimEnd == .trailing) ? startIndex : try firstIndex { !(try predicate($0)) } ?? startIndex
let rightIndex = (trimEnd == .leading) ? lastIndex : try lastIndex { !(try predicate($0)) } ?? lastIndex
return self[leftIndex ... rightIndex]
}
public func trimming(_ trimEnd: Trim = .both, where predicate: ElementEvaluationMethod) rethrows -> SubSequence {
return try trimming(trimEnd) { try predicate($0)() }
}
public func trimming(_ trimEnd: Trim = .both, while property: ElementProperty) -> SubSequence {
return trimming(trimEnd) { $0[keyPath: property] }
}
}
extension BidirectionalCollection where Element: Equatable {
public func trimming(_ trimEnd: Trim = .both, while trimElement: Element) -> SubSequence {
return trimming(trimEnd) { $0 == trimElement }
}
}
extension BidirectionalCollection where Self == SubSequence {
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluation) rethrows {
self = try trimming(trimEnd, where: predicate)
}
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluationMethod) rethrows {
self = try trimming(trimEnd, where: predicate)
}
public mutating func trim(_ trimEnd: Trim = .both, while property: ElementProperty) {
self = trimming(trimEnd, while: property)
}
}
extension BidirectionalCollection where Self == SubSequence, Element: Equatable {
public mutating func trim(_ trimEnd: Trim = .both, while trimElement: Element) {
self = trimming(trimEnd, while: trimElement)
}
}
extension StringProtocol {
public func trimming(_ trimEnd: Trim = .both) -> SubSequence {
return trimming(trimEnd, while: " ")
}
}
extension StringProtocol where Self == SubSequence {
public mutating func trim(_ trimEnd: Trim = .both) {
self = trimming(trimEnd, while: " ")
}
}
extension String {
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluation) rethrows {
self = String(try trimming(trimEnd, where: predicate))
}
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluationMethod) rethrows {
self = String(try trimming(trimEnd, where: predicate))
}
public mutating func trim(_ trimEnd: Trim = .both, while property: ElementProperty) {
self = String(trimming(trimEnd, while: property))
}
public mutating func trim(_ trimEnd: Trim = .both, while trimElement: Character = " ") {
self = String(trimming(trimEnd, while: trimElement))
}
}
extension Array {
public func trimming(_ trimEnd: Trim = .both, where predicate: ElementEvaluation) rethrows -> Array {
return Array(try trimming(trimEnd, where: predicate) as ArraySlice)
}
public func trimming(_ trimEnd: Trim = .both, where predicate: ElementEvaluationMethod) rethrows -> Array {
return Array(try trimming(trimEnd, where: predicate) as ArraySlice)
}
public func trimming(_ trimEnd: Trim = .both, while property: ElementProperty) -> Array {
return Array(trimming(trimEnd, while: property) as ArraySlice)
}
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluation) rethrows {
self = Array(try trimming(trimEnd, where: predicate))
}
public mutating func trim(_ trimEnd: Trim = .both, where predicate: ElementEvaluationMethod) rethrows {
self = Array(try trimming(trimEnd, where: predicate))
}
public mutating func trim(_ trimEnd: Trim = .both, while property: ElementProperty) {
self = Array(trimming(trimEnd, while: property))
}
}
extension Array where Element: Equatable {
public mutating func trim(_ trimEnd: Trim = .both, while trimElement: Element) {
self = Array(trimming(trimEnd, while: trimElement))
}
}
I'll include some usage in a reply below.