我正在尝试编写一个扩展方法[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)
您可以遵循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到能够使用countGenerator.Element == String使用joinWithSeparatorSubSequence.Generator.Element == String以确保dropLast(1)可以使用joinWithSeparator.dropLast(1)返回关联的类型SubSequence.Index: BidirectionalIndexType使用last.