我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
当前回答
包含相等性检查,而插入检查哈希,最安全的检查方式如下:
extension Array where Element: Hashable {
/// Big O(N) version. Updated since @Adrian's comment.
var uniques: Array {
// Go front to back, add element to buffer if it isn't a repeat.
var buffer: [Element] = []
var dictionary: [Element: Int] = [:]
for element in self where dictionary[element] == nil {
buffer.append(element)
dictionary[element] = 1
}
return buffer
}
}
其他回答
这里我对对象做了O(n)解。不是少行解决方案,而是……
struct DistinctWrapper <T>: Hashable {
var underlyingObject: T
var distinctAttribute: String
var hashValue: Int {
return distinctAttribute.hashValue
}
}
func distinct<S : SequenceType, T where S.Generator.Element == T>(source: S,
distinctAttribute: (T) -> String,
resolution: (T, T) -> T) -> [T] {
let wrappers: [DistinctWrapper<T>] = source.map({
return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0))
})
var added = Set<DistinctWrapper<T>>()
for wrapper in wrappers {
if let indexOfExisting = added.indexOf(wrapper) {
let old = added[indexOfExisting]
let winner = resolution(old.underlyingObject, wrapper.underlyingObject)
added.insert(DistinctWrapper(underlyingObject: winner, distinctAttribute: distinctAttribute(winner)))
} else {
added.insert(wrapper)
}
}
return Array(added).map( { return $0.underlyingObject } )
}
func == <T>(lhs: DistinctWrapper<T>, rhs: DistinctWrapper<T>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
// tests
// case : perhaps we want to get distinct addressbook list which may contain duplicated contacts like Irma and Irma Burgess with same phone numbers
// solution : definitely we want to exclude Irma and keep Irma Burgess
class Person {
var name: String
var phoneNumber: String
init(_ name: String, _ phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
}
let persons: [Person] = [Person("Irma Burgess", "11-22-33"), Person("Lester Davidson", "44-66-22"), Person("Irma", "11-22-33")]
let distinctPersons = distinct(persons,
distinctAttribute: { (person: Person) -> String in
return person.phoneNumber
},
resolution:
{ (p1, p2) -> Person in
return p1.name.characters.count > p2.name.characters.count ? p1 : p2
}
)
// distinctPersons contains ("Irma Burgess", "11-22-33") and ("Lester Davidson", "44-66-22")
下面是SequenceType上的一个类别,它保留了数组的原始顺序,但使用Set来进行contains查找,以避免数组的contains(_:)方法上的O(n)代价。
public extension Sequence where Element: Hashable {
/// Return the sequence with all duplicates removed.
///
/// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234, as
/// per @Alexander's comment.
func uniqued() -> [Element] {
var seen = Set<Element>()
return self.filter { seen.insert($0).inserted }
}
}
如果你不是Hashable或Equatable,你可以传入一个谓词来进行相等性检查:
extension Sequence {
/// Return the sequence with all duplicates removed.
///
/// Duplicate, in this case, is defined as returning `true` from `comparator`.
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234
func uniqued(comparator: @escaping (Element, Element) throws -> Bool) rethrows -> [Element] {
var buffer: [Element] = []
for element in self {
// If element is already in buffer, skip to the next element
if try buffer.contains(where: { try comparator(element, $0) }) {
continue
}
buffer.append(element)
}
return buffer
}
}
现在,如果你没有Hashable,但是是Equatable,你可以使用这个方法:
extension Sequence where Element: Equatable {
/// Return the sequence with all duplicates removed.
///
/// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234
func uniqued() -> [Element] {
return self.uniqued(comparator: ==)
}
}
最后,你可以添加一个unique的关键路径版本,如下所示:
extension Sequence {
/// Returns the sequence with duplicate elements removed, performing the comparison using the property at
/// the supplied keypath.
///
/// i.e.
///
/// ```
/// [
/// MyStruct(value: "Hello"),
/// MyStruct(value: "Hello"),
/// MyStruct(value: "World")
/// ].uniqued(\.value)
/// ```
/// would result in
///
/// ```
/// [
/// MyStruct(value: "Hello"),
/// MyStruct(value: "World")
/// ]
/// ```
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234
///
func uniqued<T: Equatable>(_ keyPath: KeyPath<Element, T>) -> [Element] {
self.uniqued { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
}
}
你可以把这两个都放在你的应用程序中,Swift会根据你的序列的迭代器选择正确的一个。元素类型。
对于El Capitan,您可以扩展此方法以包括多个键盘,如下所示:
/// Returns the sequence with duplicate elements removed, performing the comparison using the property at
/// the supplied keypaths.
///
/// i.e.
///
/// ```
/// [
/// MyStruct(value1: "Hello", value2: "Paula"),
/// MyStruct(value1: "Hello", value2: "Paula"),
/// MyStruct(value1: "Hello", value2: "Bean"),
/// MyStruct(value1: "World", value2: "Sigh")
/// ].uniqued(\.value1, \.value2)
/// ```
/// would result in
///
/// ```
/// [
/// MyStruct(value1: "Hello", value2: "Paula"),
/// MyStruct(value1: "Hello", value2: "Bean"),
/// MyStruct(value1: "World", value2: "Sigh")
/// ]
/// ```
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234
///
func uniqued<T: Equatable, U: Equatable>(_ keyPath1: KeyPath<Element, T>, _ keyPath2: KeyPath<Element, U>) -> [Element] {
self.uniqued {
$0[keyPath: keyPath1] == $1[keyPath: keyPath1] && $0[keyPath: keyPath2] == $1[keyPath: keyPath2]
}
}
但是(恕我直言)你最好把你自己的block传递给self.unique。
包含相等性检查,而插入检查哈希,最安全的检查方式如下:
extension Array where Element: Hashable {
/// Big O(N) version. Updated since @Adrian's comment.
var uniques: Array {
// Go front to back, add element to buffer if it isn't a repeat.
var buffer: [Element] = []
var dictionary: [Element: Int] = [:]
for element in self where dictionary[element] == nil {
buffer.append(element)
dictionary[element] = 1
}
return buffer
}
}
您总是可以使用Dictionary,因为Dictionary只能保存惟一的值。例如:
var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"]
var datesOnlyDict = NSMutableDictionary()
var x = Int()
for (x=0;x<(arrayOfDates.count);x++) {
let date = arrayOfDates[x] as String
datesOnlyDict.setValue("foo", forKey: date)
}
let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"]
println(uniqueDatesArray.count) // = 3
正如你所看到的,生成的数组并不总是按“顺序”排列。如果你想对数组排序,添加这个:
var sortedArray = sorted(datesOnlyArray) {
(obj1, obj2) in
let p1 = obj1 as String
let p2 = obj2 as String
return p1 < p2
}
println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]
.
Daniel Krom的Swift 2答案的更简洁的语法版本,使用了一个尾随闭包和简写参数名,这似乎是基于Airspeed Velocity的原始答案:
func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
var seen = [E: Bool]()
return source.filter { seen.updateValue(true, forKey: $0) == nil }
}
实现一个可以与uniq(_:)一起使用的自定义类型的示例(必须符合Hashable,因此符合Equatable,因为Hashable扩展了Equatable):
func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool {
return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty
}
struct SomeCustomType {
let id: Int
// ...
}
extension SomeCustomType: Hashable {
var hashValue: Int {
return id
}
}
在上面的代码中…
在==重载中使用的id可以是任何Equatable类型(或返回Equatable类型的方法,例如someMethodThatReturnsAnEquatableType())。注释掉的代码演示了扩展相等性检查,其中someOtherEquatableProperty是Equatable类型的另一个属性(但也可以是返回Equatable类型的方法)。
在hashValue计算属性中使用的id(必须符合Hashable)可以是任何Hashable(因此是Equatable)属性(或返回Hashable类型的方法)。
使用uniq(_:)的示例:
var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)]
print(someCustomTypes.count) // 4
someCustomTypes = uniq(someCustomTypes)
print(someCustomTypes.count) // 3