我可能有一个像下面这样的数组:
[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是否提供了一种容易做到这一点的方法,还是我必须自己做?
当前回答
在数组中保留唯一值和排序。
(使用Swift 3)
var top3score: [Int] = []
outerLoop: for i in 0..<top10score.count {
dlog(message: String(top10score[i]))
if top3score.count == 3 {
break
}
for aTop3score in top3score {
if aTop3score == top10score[i] {
continue outerLoop
}
}
top3score.append(top10score[i])
}
print("top10score is \(top10score)") //[14, 5, 5, 5, 3, 3, 2, 2, 2, 2]
print("top3score is \(top3score)") //[14, 5, 3]
其他回答
如果你需要值排序,这是工作(Swift 4)
let sortedValues = Array(Set(Array)).sorted()
这里我对对象做了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")
斯威夫特4
保证继续订购。
extension Array where Element: Equatable {
func removingDuplicates() -> Array {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
像函数式程序员一样思考:)
要根据元素是否已经出现来筛选列表,需要索引。可以使用enumeration获取索引,并使用map返回值列表。
let unique = myArray
.enumerated()
.filter{ myArray.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
这保证了秩序。如果你不介意顺序,那么Array(Set(myArray))的现有答案更简单,可能更有效。
更新:一些关于效率和正确性的注意事项
一些人对效率进行了评论。我肯定是先写正确而简单的代码,然后再找出瓶颈,尽管我知道这是否比Array(Set(Array))更清楚是有争议的。
这个方法比Array(Set(Array))慢很多。正如评论中所指出的,它确实保持了顺序,并对非Hashable的元素起作用。
然而,@Alain T的方法也保持了秩序,也快得多。所以除非你的元素类型是不可哈希的,或者你只是需要一个快速的一行,那么我建议采用他们的解决方案。
以下是MacBook Pro(2014)在Xcode 11.3.1 (Swift 5.1)发布模式下的一些测试。
profiler函数和两个比较方法:
func printTimeElapsed(title:String, operation:()->()) {
var totalTime = 0.0
for _ in (0..<1000) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
totalTime += timeElapsed
}
let meanTime = totalTime / 1000
print("Mean time for \(title): \(meanTime) s")
}
func method1<T: Hashable>(_ array: Array<T>) -> Array<T> {
return Array(Set(array))
}
func method2<T: Equatable>(_ array: Array<T>) -> Array<T>{
return array
.enumerated()
.filter{ array.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
}
// Alain T.'s answer (adapted)
func method3<T: Hashable>(_ array: Array<T>) -> Array<T> {
var uniqueKeys = Set<T>()
return array.filter{uniqueKeys.insert($0).inserted}
}
以及少量的测试输入:
func randomString(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
let shortIntList = (0..<100).map{_ in Int.random(in: 0..<100) }
let longIntList = (0..<10000).map{_ in Int.random(in: 0..<10000) }
let longIntListManyRepetitions = (0..<10000).map{_ in Int.random(in: 0..<100) }
let longStringList = (0..<10000).map{_ in randomString(1000)}
let longMegaStringList = (0..<10000).map{_ in randomString(10000)}
给出输出:
Mean time for method1 on shortIntList: 2.7358531951904296e-06 s
Mean time for method2 on shortIntList: 4.910230636596679e-06 s
Mean time for method3 on shortIntList: 6.417632102966309e-06 s
Mean time for method1 on longIntList: 0.0002518167495727539 s
Mean time for method2 on longIntList: 0.021718120217323302 s
Mean time for method3 on longIntList: 0.0005312927961349487 s
Mean time for method1 on longIntListManyRepetitions: 0.00014377200603485108 s
Mean time for method2 on longIntListManyRepetitions: 0.0007293639183044434 s
Mean time for method3 on longIntListManyRepetitions: 0.0001843773126602173 s
Mean time for method1 on longStringList: 0.007168249964714051 s
Mean time for method2 on longStringList: 0.9114790915250778 s
Mean time for method3 on longStringList: 0.015888616919517515 s
Mean time for method1 on longMegaStringList: 0.0525397013425827 s
Mean time for method2 on longMegaStringList: 1.111266262292862 s
Mean time for method3 on longMegaStringList: 0.11214958941936493 s
这在Swift 4中是有效的,如果你不想/需要将结果转换为数组,但可以使用Set。默认情况下,结果没有排序,但是可以使用sorted()这样做,它返回一个数组,如print语句所示。
let array = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var result = Set<Int>()
_ = array.map{ result.insert($0) }
print(result.sorted()) // [1, 2, 4, 6, 15, 24, 60]