[String]的Swift扩展名?

时间:2016-07-23 19:19:05

标签: swift

我试图为[String]编写扩展方法。

您似乎无法直接延长[String]("类型'元素'约束到非协议类型'字符串'"虽然我遇到了这个伎俩:

protocol StringType { }
extension String: StringType { }

但我仍然无法让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!
        }
    }
}

joinWithSeparator来电是#34;不明确的"。我已经尝试了我能想到的一切,比如使用(self as! [String])(以及一些类似的变体),但似乎没有任何效果。

如何让Swift编译器对此感到满意?

2 个答案:

答案 0 :(得分:10)

编辑/更新

Swift 4 或更高版本最好将集合元素约束为StringProtocol,这也将涵盖子字符串。

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 ")
    }
}

如果所有元素都只是字符,我们可以简单地扩展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 + " ")
    }
}
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"

原始答案

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 ")
    }
}
["a","b","c"].joinedWithCommas    // "a, b, or c"

答案 1 :(得分:6)

您可以按照joinWithSeparator的声明(Cmd点击它),发现它被定义为协议SequenceType的扩展,而不是类型Array

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

注意:在Xcode 8 / Swift 3中如果您点击join(separator:),即使still implemented inside Sequence,您也会登陆Array,但是这不会使下面的想法失效)

我们可以对你的函数做同样的事情,我们扩展了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!
        }
    }
}

注意:

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