[String]的Swift扩展名?

J. *_*coe 8 swift

我正在尝试编写一个扩展方法[String].

看起来你不能[String]直接扩展("Type'Element'约束到非协议类型'String'"),虽然我遇到了这个伎俩:

protocol StringType { }
extension String: StringType { }
Run Code Online (Sandbox Code Playgroud)

但我还是不能让Swift类型的系统满意:

extension Array where Element: StringType {
    // ["a","b","c","d","e"] -> "a, b, c, d, or e".
    func joinWithCommas() -> String {
        switch count {
        case 0, 1, 2:
            return joinWithSeparator(" or ")
        default:
            return dropLast(1).joinWithSeparator(", ") + ", or " + last!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

joinWithSeparator电话是"暧昧".我已经尝试了我能想到的一切,比如使用(self as! [String])(以及一些类似的变体),但似乎没有任何效果.

如何让Swift编译器满意呢?

Leo*_*bus 10

编辑/更新

Swift 4或更高版本最好将集合元素约束为StringProtocol,它也将覆盖Substrings.

extension BidirectionalCollection where Element: StringProtocol {
    var joinedWithCommas: String {
        guard let last = last else { return "" }
        return count > 2 ? dropLast().joined(separator: ", ") + ", or " + last : joined(separator: " or ")
    }
}
Run Code Online (Sandbox Code Playgroud)

如果所有元素都只是字符,我们可以简单地扩展StringProtocol:

extension StringProtocol {
    func joined(with separator: String = ",", conector: String = "") -> String {
        guard let last = last else { return "" }
        if count > 2 {
            return dropLast().map(String.init).joined(separator: separator + " ") + separator + " " + conector + " " + String(last)
        }
        return map(String.init).joined(separator: " " + conector + " ")
    }
}
Run Code Online (Sandbox Code Playgroud)
let elements = "abc"
let elementsJoined = elements.joined()                   // "a, b, c"
let elementsSeparated = elements.joined(conector: "or")  // "a, b, or c"
let elementsConected = elements.joined(conector: "and")  // "a, b, and c"
Run Code Online (Sandbox Code Playgroud)

原始答案

Swift 3.1(Xcode 8.3.2)中,您可以简单地将Array约束元素类型扩展为String

extension Array where Element == String {
    var joinedWithCommas: String {
        guard let last = last else { return "" }
        return count > 2 ? dropLast().joined(separator: ", ") + ", or " + last : joined(separator: " or ")
    }
}
Run Code Online (Sandbox Code Playgroud)
["a","b","c"].joinedWithCommas    // "a, b, or c"
Run Code Online (Sandbox Code Playgroud)


ken*_*ytm 6

您可以遵循joinWithSeparator(Cmd-单击它)的声明,并发现它被定义为协议的扩展SequenceType而不是类型Array.

// swift 2:
extension SequenceType where Generator.Element == String {
    public func joinWithSeparator(separator: String) -> String
}
Run Code Online (Sandbox Code Playgroud)

(注意:在Xcode 8/Swift 3中,如果你点击它join(separator:),Array即使它仍然在里面实现Sequence,你也会着陆,但这不会使下面的想法无效)

我们可以对你的函数做同样的事情,我们扩展了Array采用的协议而不是Array本身:

// swift 2:
extension CollectionType where
        Generator.Element == String,
        SubSequence.Generator.Element == String,
        Index: BidirectionalIndexType
{
    func joinWithCommas() -> String {
        switch count {
        case 0, 1, 2:
            return joinWithSeparator(" or ")
        default:
            return dropLast(1).joinWithSeparator(", ") + ", or " + last!
        }
    }
}

// swift 3:
extension BidirectionalCollection where
        Iterator.Element == String,
        SubSequence.Iterator.Element == String
{
    func joinWithCommas() -> String {
        switch count {
        case 0, 1, 2:
            return joined(separator: " or ")
        default:
            return dropLast().joined(separator: ", ") + ", or " + last!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 我们扩展CollectionType到能够使用count
  • 我们约束Generator.Element == String使用joinWithSeparator
  • 我们约束SubSequence.Generator.Element == String以确保dropLast(1)可以使用joinWithSeparator.dropLast(1)返回关联的类型SubSequence.
  • 我们约束Index: BidirectionalIndexType使用last.