У меня может быть массив, который выглядит следующим образом:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
Или, действительно, любая последовательность типизированных частей данных. То, что я хочу сделать, это убедиться, что есть только один из каждого идентичного элемента. Например, приведенный выше массив станет следующим:
[1, 4, 2, 6, 24, 15, 60]
Обратите внимание, что дубликаты из 2, 6 и 15 были удалены, чтобы убедиться, что существует только один из каждого идентичного элемента. Сможет ли Свифт сделать это легко, или я должен сам это сделать?
Вы можете бросить свой собственный, например, так (обновлено для Swift 1.2 с Set):
func uniq<S : SequenceType, T : Hashable where S.Generator.Element == T>(source: S) -> [T] {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]
Версия Swift 3:
func uniq<S : Sequence, T : Hashable>(source: S) -> [T] where S.Iterator.Element == T {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
Вы можете легко конвертировать в набор и обратно в массив:
let unique = Array(Set(originals))
Это не гарантирует сохранение исходного порядка массива.
Многие ответы доступны здесь, но я пропустил это простое расширение, подходящее для Swift 2 и выше:
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
Делает это супер простым. Можно вызвать так:
let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
Фильтрация на основе свойств
Чтобы фильтровать массив на основе свойств, вы можете использовать этот метод:
extension Array {
func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(lhs: element, rhs: $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
Что вы можете назвать следующим:
let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
Это берет некоторую полезную информацию, которая уже есть на этой странице, и применяет подход Hashable/Set, когда это возможно, и в противном случае возвращается к уравнительному коду.
Swift 4 изменений для Equatable
расширения (Hashable
остается неизменным)
public extension Sequence where Element: Equatable {
var uniqueElements: [Element] {
return self.reduce(into: []) {
uniqueElements, element in
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
}
}
Свифт 3
public extension Sequence where Iterator.Element: Hashable {
var uniqueElements: [Iterator.Element] {
return Array( Set(self) )
}
}
public extension Sequence where Iterator.Element: Equatable {
var uniqueElements: [Iterator.Element] {
return self.reduce([]){
uniqueElements, element in
uniqueElements.contains(element)
? uniqueElements
: uniqueElements + [element]
}
}
}
Swift 2
public extension SequenceType where Generator.Element: Hashable {
var uniqueElements: [Generator.Element] {
return Array(
Set(self)
)
}
}
public extension SequenceType where Generator.Element: Equatable {
var uniqueElements: [Generator.Element] {
return self.reduce([]){uniqueElements, element in
uniqueElements.contains(element)
? uniqueElements
: uniqueElements + [element]
}
}
}
Swift 3.0
let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
Ограничение элементов коллекции в Equatable, которое вы можете использовать, содержит:
extension Collection where Element: Equatable {
var orderedSet: [Element] {
var array: [Element] = []
return compactMap {
if array.contains($0) {
return nil
} else {
array.append($0)
return $0
}
}
}
}
Другим вариантом является ограничение элемента коллекции на Hashable и использование набора для управления тем, какие элементы вы должны отобразить в результате:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return compactMap { set.insert($0).inserted ? $0 : nil }
}
}
используя фильтр:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
или используя NSOrderedSet:
extension Array where Element: Hashable {
var orderedSet: Array {
return NSOrderedSet(array: self).array as? Array ?? []
}
}
Используя Swift 4 уменьшить (в :)
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set: Set<Element> = []
return reduce(into: []) { set.insert($1).inserted ? $0.append($1) : () }
}
}
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Вы также можете расширить протокол RangeReplaceableCollection
, чтобы он также мог использоваться с типами StringProtocol (Strings и SubStrings):
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
mutating func removeDuplicates() {
var set = Set<Element>()
removeAll { !set.insert($0).inserted }
}
}
"abcdefabcghi".orderedSet // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Метод мутации
var string = "abcdefabcghi"
string.removeDuplicates()
string // "abcdefghi"
var substring = "abcdefabcdefghi".dropFirst(3) // "defabcdefghi"
substring.removeDuplicates()
substring // "defabcghi"
Swift 4
Гарантированно сохранить порядок.
extension Array where Element: Equatable {
func removingDuplicates() -> Array {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
Swift 4
public extension Array where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter{ seen.insert($0).inserted }
}
}
каждая попытка insert
также возвращает кортеж: (inserted: Bool, memberAfterInsert: Set.Element)
. Смотрите документацию.
Использование возвращаемого значения помогает нам избежать зацикливания или выполнения каких-либо других операций.
O(n^2)
, и никто не заметил.
Отсюда альтернативное (если не оптимальное) решение с использованием неизменяемых типов, а не переменных:
func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
let s = reduce(seq, S()){
ac, x in contains(ac,x) ? ac : ac + [x]
}
return s
}
Включено противопоставление Жана-Пилиппе императивного подхода с функциональным подходом.
В качестве бонуса эта функция работает как со строками, так и с массивами!
Изменить: Этот ответ был написан в 2014 году для Swift 1.0 (до того, как Set
был доступен в Swift). Он не требует соответствия Hashable и работает в квадратичном времени.
contains
и добавление массива выполняется в O (n). Хотя он имеет преимущество только в том, что требует равных, а не хэш.
filter
. Это O (n ^ 2) (что необходимо, если вы не хотите требовать соответствия Hashable
), но вы должны по крайней мере явно Hashable
это
Здесь категория по SequenceType
, которая сохраняет первоначальный порядок массива, но использует Set
сделать contains
Lookups, чтобы избежать O(n)
стоимость на массив contains(_:)
метод.
public extension Array where Element: Hashable {
/// Return the array 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 comment.
public func uniqued() -> [Element] {
var seen = Set<Element>()
return self.filter { seen.insert($0).inserted }
}
}
или если у вас нет Hashable, вы можете сделать это:
public extension Sequence where Iterator.Element: Equatable {
public func uniqued() -> [Iterator.Element] {
var buffer: [Iterator.Element] = []
for element in self {
guard !buffer.contains(element) else { continue }
buffer.append(element)
}
return buffer
}
}
Вы можете вставить оба из них в свое приложение, Swift выберет правильный в зависимости от типа вашей последовательности Iterator.Element
.
O(n)
. Кстати, вы можете объединить операции set проверки и вставки в одну. См. Stackoverflow.com/a/46354989/3141234
с функцией uniq:
func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter({ (v) -> Bool in
return seen.updateValue(true, forKey: v) == nil
})
}
использование:
var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9
Bool
очевидно, является избыточным, поскольку ваш код никогда не читает его. Используйте Set
вместо Dictionary
и вы получите мое повышение.
Swift 4.x:
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
return Array(Set<Iterator.Element>(self))
}
func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}
использование:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()
или же
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
O(n^2)
. Не делай этого.
Еще одно решение Swift 3.0 для удаления дубликатов из массива. Это решение улучшилось во многих других решениях, уже предложенных:
Учитывая целочисленный массив:
let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]
Функциональный код:
func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
var unique = Set<T>()
return array.filter { element in
return unique.insert(element).inserted
}
}
orderedSet(array: numberArray) // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
Код расширения Array:
extension Array where Element:Hashable {
var orderedSet: Array {
var unique = Set<Element>()
return filter { element in
return unique.insert(element).inserted
}
}
}
numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
Этот код использует результат, возвращаемый операцией insert
на Set
, которая выполняется на O(1)
, и возвращает кортеж, указывающий, был ли элемент вставлен или если он уже существует в наборе.
Если элемент был в наборе, filter
исключит его из окончательного результата.
defer
код дважды выполняет заданную тестовую операцию, одну с contains
а другую с insert
. Дальнейшее чтение Swift документации, я обнаружил , что insert
возвращает кортеж , указывающий , если элемент был вставлен или нет, поэтому я упростил код Извлечения contains
проверку.
Вы можете напрямую использовать коллекцию наборов для удаления дубликата, а затем вернуть его в массив
var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)
myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]
Затем вы можете заказать свой массив, как хотите
myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: [Iterator.Element: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: $0) == nil }
}
}
extension Sequence { // Returns distinct elements based on a key value. func distinct<key: Hashable>(by: ((_ el: Iterator.Element) -> key)) -> [Iterator.Element] { var existing = Set<key>() return self.filter { existing.insert(by($0)).inserted } } }
Для массивов, где элементы не являются ни Hashable, ни Comparable (например, сложные объекты, словари или структуры), это расширение предоставляет обобщенный способ удаления дубликатов:
extension Array
{
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
{
var uniqueKeys = Set<String>()
return filter{uniqueKeys.insert("\(keyValue($0))").inserted}
}
}
// example usage: (for a unique combination of attributes):
peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }
Вам не нужно беспокоиться о создании значений Hashable и позволяет использовать разные комбинации полей для уникальности.
"\()"
, так как оно может не дать вам уникальные значения, такие как соответствие Hashable
. Например, если все ваши элементы соответствуют Printable
и все они возвращают одно и то же description
, ваша фильтрация завершится неудачно.
Немного более сжатая версия синтаксиса Даниэль Кром Свифт 2 ответ, используя закрывающее закрытие и сокращенное имя аргумента, которое, по-видимому, основано на Первоначальный ответ скорости воздушной скорости:
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
extends 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
).
id
, используемый в hashValue
вычисленном свойстве (требуется для соответствия 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
Если вам нужны отсортированные значения, это работает (Swift 4)
let sortedValues = Array(Set(array)).sorted()
.sorted()
в конце. С уважением.
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
if !answer1.contains(i) {
answer1.append(i)
}}
return answer1
}
Использование:
let f = removeDublicate(ab: [1,2,2])
print(f)
Позвольте мне предложить ответ, похожий на ответ Скотта Гарднера, но с более лаконичным синтаксисом с использованием сокращения. Это решение удаляет дубликаты из массива пользовательских объектов (сохраняя начальный порядок)
// Custom Struct. Can be also class.
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
let name: String
let lastName : String
}
// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}
let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
CustomStruct(name: "name2", lastName: "lastName1"),
CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3
// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed
И только если вам интересно, как это работает магия магии - здесь точно то же самое, но с использованием более расширенного синтаксиса сокращения
let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
var newResult = result
if (newResult.contains(category)) {}
else {
newResult.append(category)
}
return newResult
}
uniq2.count // prints 2 - third element has removed
Вы можете просто скопировать этот код в Swift Playground и поиграть.
$0
на $0.0
и $1
на $0.1
.
reduce
с изменяемым типом структуры (здесь это []), поскольку изменчивость не сохраняется, а reduce
создает накладные расходы на копирование структуры каждый раз.
Я использовал ответ @Jean-Philippe Pellet и сделал расширение Array, которое выполняет операции типа на массивах, сохраняя порядок элементов.
/// Extensions for performing set-like operations on lists, maintaining order
extension Array where Element: Hashable {
func unique() -> [Element] {
var seen: [Element:Bool] = [:]
return self.filter({ seen.updateValue(true, forKey: $0) == nil })
}
func subtract(takeAway: [Element]) -> [Element] {
let set = Set(takeAway)
return self.filter({ !set.contains($0) })
}
func intersect(with: [Element]) -> [Element] {
let set = Set(with)
return self.filter({ set.contains($0) })
}
}
здесь я сделал некоторое 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")
Set
с пользовательским DistinctWrapper
, вы должны использовать Dictionary
от visibleAttributes для объектов. Когда вы будете следовать этой логике, вы в конечном итоге будете реализовывать [ Dictionary.init(_:uniquingKeysWith:)
] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… , который теперь встроен в Стандартная библиотека. Проверьте, насколько это просто pastebin.com/w90pVe0p
Вы всегда можете использовать словарь, потому что словарь может содержать только уникальные значения. Например:
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"]
.
это самый простой способ в Swift 4.2 и далее код, как показано ниже
let keyarray:NSMutableArray = NSMutableArray()
for object in dataArr
{
if !keysArray.contains(object){
keysArray.add(object)
}
}
print(keysArray)
O(n^2)
(потому что contains
O(n)
, который запускается n
раз). И не используйте NSMutableArray в Swift
Для этого я сделал простое расширение.
extension Array where Element: Equatable {
func containsHowMany(_ elem: Element) -> Int {
return reduce(0) { $1 == elem ? $0 + 1 : $0 }
}
func duplicatesRemoved() -> Array {
return self.filter { self.containsHowMany($0) == 1 }
}
mutating func removeDuplicates() {
self = self.duplicatesRemoved(()
}
}
Вы можете использовать duplicatesRemoved()
чтобы получить новый массив, дубликаты которого удалены, или removeDuplicates()
чтобы removeDuplicates()
его сам. Увидеть:
let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
let noDuplicates = arr.duplicatesRemoved()
print(arr) // [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
print(noDuplicates) // [1, 2, 3, 4, 5, 6, 7, 8]
arr.removeDuplicates()
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
O(n²)
.
В Swift 3.0 самое простое и быстрое решение, которое я нашел для устранения дублированных элементов при сохранении порядка:
extension Array where Element:Hashable {
var unique: [Element] {
var set = Set<Element>() //the unique list kept in a Set for fast retrieval
var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
for value in self {
if !set.contains(value) {
set.insert(value)
arrayOrdered.append(value)
}
}
return arrayOrdered
}
}
contains
и insert
в один выстрел: stackoverflow.com/a/46354989/3141234
Swift 3
На основе ответа Jean-Philippe Pellet, я обновил его синтаксис для Swift 3.
func uniq<S : Sequence, T : Hashable where S.Iterator.Element == T>(source: S) -> [T] {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(source: vals) // [1, 4, 2, 6, 24, 15, 60]
Это просто очень простая и удобная реализация. Вычисленное свойство в расширении массива, имеющего эквивалентные элементы.
extension Array where Element: Equatable {
/// Array containing only _unique_ elements.
var unique: [Element] {
var result: [Element] = []
for element in self {
if !result.contains(element) {
result.append(element)
}
}
return result
}
}
if
условие здесь имеет смысл, чем охранник.
Я считаю, что было бы неплохо предложить функции uniq()
и uniqInPlace()
, чтобы мутировать массив, удалив его. Это работает аналогично функции sort()
и sortInPlace()
, предоставляемой Swift. Кроме того, поскольку он представляет собой массив, он должен сохранять исходный порядок элементов.
extension Array where Element: Equatable {
public func uniq() -> [Element] {
var arrayCopy = self
arrayCopy.uniqInPlace()
return arrayCopy
}
mutating public func uniqInPlace() {
var seen = [Element]()
var index = 0
for element in self {
if seen.contains(element) {
removeAtIndex(index)
} else {
seen.append(element)
index++
}
}
}
}
Вы можете использовать только uniqInPlace()
для переменной Array (т.е. var
), поскольку вы не можете мутировать постоянный массив (т.е. let
).
Некоторые примеры использования:
var numbers = [1, 6, 2, 2, 4, 1, 5]
numbers.uniqInPlace() // array is now [1, 6, 2, 4, 5]
let strings = ["Y", "Z", "A", "Y", "B", "Y", "Z"]
let uniqStrings = strings.uniq() // uniqStrings is now ["Y", "Z", "A", "B"]
Array<Element>
не является подходящим выбором для данного типа seen
. Повторяющиеся вызовы contains
(каждый из которых O(n)
) делает этот алгоритм по крайней мере O(n^2)
. Кроме того, removeAtIndex
) также равно O(n)
(потому что каждый элемент после элемента удаления должен быть смещен влево на 1). Вместо этого алгоритм, подобный этому, будет работать лучше, используя var seen = Set<Element>()
, и вместо того, чтобы «удалять» элементы, вы «перезаписываете» их, читая вперед, пока не увидите следующий элемент, который вам нужно сохранить.
Вот ответ, который я придумал после поиска в Интернете и не находил того, что искал. Используя Set, вы можете добавить все элементы с уменьшением. Затем я беру результат и преобразовываю его в отсортированный массив.
let initialArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let distinct2 = initialArray.reduce(Set<Int>(), combine: { (set, current) -> Set<Int> in
var tmp = set
tmp.insert(current)
return tmp
})
// distinct2 is now a set containing {2, 4, 60, 6, 15, 24, 1}
// Make it into a sorted array
let sorted = Array(distinct2).sorted(<) // Returns [1, 2, 4, 6, 15, 24, 60]
func removeDuplicates(_ nums: inout [Int]) -> Int {
nums = Set(nums).sorted()
return nums.count
}
пример
var arr = [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
removeDuplicates(&arr)
print(arr) // [1,2,3,4,5,6,7,8,9]
Это работает в Swift 4, если вы не хотите/не хотите конвертировать результат в массив, но можете делать это с множеством. Результат не сортируется по умолчанию, но вы можете сделать это с помощью функции sorted(), которая возвращает массив, как показано в операторе печати.
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]
map
где forEach
будет иметь больше смысла. Тем не менее, это может быть просто let result = Set(array)
Самый простой способ - использовать NSOrderedSet, который хранит уникальные элементы и сохраняет порядок элементов. Подобно:
func removeDuplicates(from items: [Int]) -> [Int] {
let uniqueItems = NSOrderedSet(array: items)
return (uniqueItems.array as? [Int]) ?? []
}
let arr = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
removeDuplicates(from: arr)
Здесь более гибкий способ сделать последовательность уникальной с помощью пользовательской функции сопоставления.
extension Sequence where Iterator.Element: Hashable {
func unique(matching: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {
var uniqueArray: [Iterator.Element] = []
forEach { element in
let isUnique = uniqueArray.reduce(true, { (result, item) -> Bool in
return result && matching(element, item)
})
if isUnique {
uniqueArray.append(element)
}
}
return uniqueArray
}
}
contains(where:)
могут быть реализованы с помощью только reduce
, но и в производстве код , как это, это лучше всего использовать contains(where:)
напрямую. С одной стороны, он может замкнуть поиск, если элемент найден на ранней стадии (нет необходимости продолжать поиск после этого, но этот метод reduce
делает это в любом случае)
Сохранять уникальные значения и сохранять сортировку в массиве.
(используя 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]
3
результатов), и нет простого способа повторно использовать его без массового копирования / вставки. Вы должны взглянуть на этот ответ . Затем вы могли бы написать что-то вроде scores.uniqued().prefix(3)
. Просто!
Swift 3.0
extension Array where Element: Equatable {
func withoutDuplicates() -> [Element] {
return self.reduce([], { (objects, object) -> [Element] in
var objects = objects
if !objects.contains(object) {
objects.append(object)
}
return objects
})
}
}
NSSet
, NSSet - это неупорядоченная коллекция объектов, если необходимо сохранить порядок NSOrderedSet.