В настоящее время в Swift вы просто печатаете, Set( yourArray )
чтобы сделать массив уникальным. (Или заказанный набор при необходимости.)
До того, как это было возможно, как это было сделано?
Я мог бы иметь массив, который выглядит следующим образом:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
Или, на самом деле, любая последовательность однотипных частей данных. Что я хочу сделать, это убедиться, что есть только один из каждого идентичного элемента. Например, приведенный выше массив станет:
[1, 4, 2, 6, 24, 15, 60]
Обратите внимание, что дубликаты 2, 6 и 15 были удалены, чтобы гарантировать, что был только один из каждого идентичного элемента. Предоставляет ли Swift способ сделать это легко, или мне придется делать это самому?
arrays
swift
standard-library
Altair357
источник
источник
NSSet
NSSet - это неупорядоченная коллекция объектов, если необходимо сохранить порядок NSOrderedSet.$.uniq(array)
github.com/ankurp/Dollar#uniq---uniqSet
от Swift? Вы сможете предоставить список неупорядоченных и уникальных элементов.Ответы:
Вы можете бросить свой собственный, например, так ( обновлено для Swift 1.2 с Set ):
Версия Swift 3:
И как расширение для
Array
:источник
var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
updateValue(true, forKey: $0)...
вместоaddedDict(true, forKey: $0)...
return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }
как вы говорите.let uniques = Array(Set(vals))
Вы можете легко преобразовать в набор и снова вернуться в массив:
Это не гарантирует сохранение исходного порядка массива.
источник
originals
не являютсяHashable
;Hashable
в набор могут быть добавлены только типы данных, но в массив может быть добавлен любой тип данных.Здесь доступно много ответов, но я пропустил это простое расширение, подходящее для Swift 2 и выше:
Делает это очень просто. Можно назвать так:
Фильтрация по свойствам
Чтобы отфильтровать массив на основе свойств, вы можете использовать этот метод:
Который вы можете позвонить следующим образом:
источник
extension Array where Element: Equatable
) заменяется stackoverflow.com/a/36048862/1033581, который предлагает более мощное решение (extension Sequence where Iterator.Element: Equatable
).O(n²)
временную производительность, что очень плохо для больших массивов.O(n²)
сложность кO(n)
Swift 3.0
источник
array
не являютсяHashable
;Hashable
в набор могут быть добавлены только типы данных, но в массив может быть добавлен любой тип данных.Если вы добавите оба расширения в свой код, по возможности
Hashable
будет использоваться более быстрая версия, аEquatable
версия будет использоваться в качестве запасного варианта.Если порядок не важен, то вы всегда можете просто использовать этот инициализатор Set .
источник
O(n²)
временную производительность, что очень плохо для больших массивов.редактировать / обновлять Swift 4 или новее
Мы также можем расширить
RangeReplaceableCollection
протокол, чтобы использовать его сStringProtocol
типами:Метод мутации:
Для Swift 3 нажмите здесь
источник
reduce
реализацию, так что теперь сложность другая.O(n)
времени), тогда как версия для плоской карты занимает в 7,47 раза больше для 8 миллионов уникальных записей, чем для 1 миллиона, предполагая, что версия на основе плоской карты масштабируется лучше , Каким-то образом версия на основе плоской карты работает немного лучше, чемO(n)
время!Swift 4
каждая попытка
insert
также вернет кортеж(inserted: Bool, memberAfterInsert: Set.Element)
. Смотрите документацию .Использование возвращаемого значения помогает нам избежать зацикливания или выполнения каких-либо других операций.
источник
O(n^2)
, и никто не заметил.Swift 4
Гарантированно сохранить заказ.
источник
reduce
: в целом, это просто еще одна линия в целом ваш проект , чтобы написать вашу функцию:var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique
. Я признаю, что еще не тестировал относительные характеристики.O(n²)
временную производительность, что очень плохо для больших массивов.O(n²)
. В этом нет ничего быстрого.reduce
илиreduce(into:)
не будет иметь решающее значение. Переписав это, чтобы не повторять вызов,contains
будет ОЧЕНЬ большая разница.Вот категория по
SequenceType
которой сохраняет первоначальный порядок массива, но использует ,Set
чтобы сделатьcontains
Lookups , чтобы избежатьO(n)
затрат на массивcontains(_:)
метода.Если вы не Hashable или Equatable, вы можете передать предикат для проверки равенства:
Теперь, если у вас нет Hashable, но являются Equatable, вы можете использовать этот метод:
Наконец, вы можете добавить ключевую версию uniqued следующим образом:
Вы можете вставить оба из них в свое приложение, Swift выберет правильный в зависимости от типа вашей последовательности
Iterator.Element
.источник
O(n)
решением. Кстати, вы можете объединить операции set проверки и вставки в одну. См. Stackoverflow.com/a/46354989/3141234Вдохновленный https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift , мы можем объявить более мощный инструмент, способный фильтровать уникальность для любого keyPath. Благодаря комментариям Александра к различным ответам относительно сложности, приведенные ниже решения должны быть почти оптимальными.
Не мутирующее решение
Мы расширили функцию, которая способна фильтровать уникальность для любого keyPath:
Примечание: в случае, если ваш объект не соответствует RangeReplaceableCollection, но соответствует Sequence, вы можете иметь это дополнительное расширение, но тип возвращаемого значения всегда будет Array:
использование
Если мы хотим уникальности для самих элементов, как в вопросе, мы используем keyPath
\.self
:Если мы хотим уникальности для чего-то другого (например, для
id
коллекции объектов), тогда мы используем keyPath по нашему выбору:Мутирующий раствор
Мы расширяем его мутирующей функцией, которая способна фильтровать уникальность для любого keyPath:
использование
Если мы хотим уникальности для самих элементов, как в вопросе, мы используем keyPath
\.self
:Если мы хотим уникальности для чего-то другого (например, для
id
коллекции объектов), тогда мы используем keyPath по нашему выбору:источник
keyPath
умолчанию\.self
, потому что это, вероятно, большинство вариантов использования.Element
всегдаHashable
. Альтернативой значению по умолчанию является добавление простой перегрузки без параметров:extension Sequence where Element: Hashable { func unique() { ... } }
Отсюда альтернативное (если не оптимальное) решение с использованием неизменяемых типов, а не переменных:
Включенный, чтобы противопоставить императивный подход Жана-Пилиппа с функциональным подходом.
В качестве бонуса эта функция работает как со строками, так и с массивами!
Изменить: Этот ответ был написан в 2014 году для Swift 1.0 (до того, как
Set
был доступен в Swift). Не требует соответствия Hashable и работает в квадратичном времени.источник
contains
и добавление массива, запускаются в O (n). Хотя он имеет преимущество только в том, что требует равных, а не хэш.filter
. Это O (n ^ 2) (что необходимо, если вы не хотите требоватьHashable
соответствия), но вы должны по крайней мере явно заявить об этомСвифт 2
с uniq функцией ответа:
использовать:
источник
Bool
Значение , очевидно , является излишним, так как ваш код никогда не читает. ИспользуйтеSet
вместо a,Dictionary
и вы получите мой upvote.В Свифт 5
Выход будет
источник
Еще одно решение Swift 3.0 для удаления дубликатов из массива. Это решение улучшает многие другие решения, уже предложенные:
Учитывая целочисленный массив:
Функциональный код:
Код расширения массива:
Этот код использует в своих интересах результат, возвращаемый
insert
операцией onSet
, которая выполняетсяO(1)
и возвращает кортеж, указывающий, был ли элемент вставлен или уже существовал в наборе.Если предмет был в наборе,
filter
исключим его из окончательного результата.источник
defer
помощью кода дважды выполнялась бы заданная тестовая операция: одна с,contains
а другая сinsert
. Продолжая читать документацию по Swift, я обнаружил, чтоinsert
возвращает кортеж, указывающий, был ли элемент вставлен или нет, поэтому я упростил код, удаливcontains
проверку.extension Sequence where Iterator.Element: Hashable { ... }
insert
иcontains
имеютO(1)
сложность.O(1) + O(1) = O(1)
, Затем эти две операции выполняютсяn
раз (один раз на вызов переданного замыканияfilter
, который вызывается один раз на элемент). Т.е., если операция занимает постоянное количество времени независимо от размера ввода, то выполнение ее дважды все равно заставляет ее занимать постоянное время. это независимо от размера ввода. Общая сложность этого естьO(n)
.Swift 4.x:
использование:
или
источник
O(n^2)
. Не делай этого.Swift 5
источник
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 } } }
Bool
, когда единственное значение, которое вы используетеtrue
. Вы достигаете "тип единицы" (тип только с одним возможным значением). Тип модуля Свифта - этоVoid
единственное значение()
(или пустой кортеж). Так что вы можете просто использовать[T: Void]
. Хотя вы не должны этого делать, потому что вы в основном только что изобрелиSet
. ИспользуйтеSet
вместо этого. См. Stackoverflow.com/a/55684308/3141234 Пожалуйста, удалите этот ответ.Думай как функциональный программист :)
Чтобы отфильтровать список на основе того, был ли элемент уже найден, вам нужен индекс. Вы можете использовать,
enumerated
чтобы получить индекс иmap
вернуться к списку значений.Это гарантирует порядок. Если вы не возражаете против порядка, то существующий ответ
Array(Set(myArray))
проще и, вероятно, более эффективен.ОБНОВЛЕНИЕ: некоторые примечания по эффективности и правильности
Несколько человек прокомментировали эффективность. Я определенно в школе сначала пишу правильный и простой код, а потом выясняю узкие места, хотя я ценю спор о том, яснее ли это
Array(Set(array))
.Этот метод намного медленнее, чем
Array(Set(array))
. Как отмечается в комментариях, он сохраняет порядок и работает с элементами, которые не являются Hashable.Тем не менее, метод @Alain T также сохраняет порядок и работает намного быстрее. Так что, если ваш тип элемента не является хэшируемым, или вам просто нужен быстрый однострочник, я бы посоветовал перейти к их решению.
Вот несколько тестов на MacBook Pro (2014) на Xcode 11.3.1 (Swift 5.1) в режиме выпуска.
Функция профилирования и два метода для сравнения:
И небольшое разнообразие тестовых входов:
Дает в качестве вывода:
источник
Array(Set(myArray))
этого, это работает для вещей, которые не являютсяHashable
Array(Set(myArray))
от порядка вашего массива поддерживается.lastIndex(of:)
. Я полностью не согласен по поводу ясности и оптимизации в этом случае. Я не думаю, что эта реализация особенно ясна, особенно по сравнению с простым решением на основе множеств. В любом случае, такой код должен быть извлечен в функцию расширения. Этот алгоритм становится практически непригодным даже при небольшом входном размере, например, от тысяч до десятков тысяч. Нетрудно найти такие наборы данных, у людей могут быть тысячи песен, файлов, контактов и т. Д.Для массивов, в которых элементы не являются ни Hashable, ни Comparable (например, сложные объекты, словари или структуры), это расширение предоставляет обобщенный способ удаления дубликатов:
Вам не нужно беспокоиться о создании значений Hashable, и это позволяет вам использовать различные комбинации полей для уникальности.
Примечание: для более надежного подхода см. Решение, предложенное Coeur, в комментариях ниже.
stackoverflow.com/a/55684308/1033581
[Редактировать] Swift 4 альтернативы
В Swift 4.2 вы можете использовать класс Hasher для создания хешей. Вышеупомянутое расширение может быть изменено, чтобы использовать это:
Синтаксис вызова немного отличается, потому что замыкание получает дополнительный параметр, содержащий функцию для хэширования переменного числа значений (которое должно быть по отдельности Hashable)
Он также будет работать с одним значением уникальности (используя $ 1 и игнорируя $ 0).
источник
"\()"
, так как это может не дать вам уникальные значения, такие как соответствиеHashable
следует. Например, если ваши элементы соответствуютPrintable
всем, возвращающим одноdescription
и то же , тогда ваша фильтрация завершается ошибкой.T
бытиемHashable
.Вы можете напрямую использовать коллекцию наборов для удаления дубликатов, а затем преобразовать ее обратно в массив
Тогда вы можете заказать свой массив, как вы хотите
источник
Чуть более краткая синтаксическая версия ответа Даниэля Крома Swift 2 с использованием завершающего замыкания и сокращенного имени аргумента, которое, похоже, основано на первоначальном ответе Airspeed Velocity :
Пример реализации пользовательского типа, который может использоваться с
uniq(_:)
(который должен соответствоватьHashable
и, следовательноEquatable
, потому чтоHashable
расширяетсяEquatable
):В приведенном выше коде ...
id
, как используется в перегрузке==
, может быть любогоEquatable
типа (или метода, который возвращаетEquatable
тип, например,someMethodThatReturnsAnEquatableType()
). Закомментированный код демонстрирует расширение проверки на равенство, гдеsomeOtherEquatableProperty
есть другое свойствоEquatable
типа (но также может быть методом, который возвращаетEquatable
тип).id
как используется вhashValue
вычисляемом свойстве (требуется для соответствияHashable
), может быть любымHashable
(и, следовательно,Equatable
) свойством (или методом, возвращающимHashable
тип).Пример использования
uniq(_:)
:источник
Bool
, когда единственное значение, которое вы используетеtrue
. Вы достигаете "тип единицы" (тип только с одним возможным значением). Тип модуля Свифта - этоVoid
единственное значение()
(или пустой кортеж). Так что вы можете просто использовать[T: Void]
. Хотя вы не должны этого делать, потому что вы в основном только что изобрелиSet
. ИспользуйтеSet
вместо этого. См. Stackoverflow.com/a/55684308/3141234Если вам нужны отсортированные значения, это работает (Swift 4)
let sortedValues = Array(Set(array)).sorted()
источник
.sorted()
конец. С уважением.[2, 1, 1]
? Это вышло бы[1, 2]
, это не заказано: p[2, 1, 1]
. Первое появление уникальных элементов в порядке[2, 1]
. Это правильный ответ. Но с помощью (неправильный) алгоритм, вы получите[1, 2]
, который будет отсортирован, но не в правильном, оригинал, заказ.array
не являютсяHashable
;Hashable
в набор могут быть добавлены только типы данных, но в массив может быть добавлен любой тип данных.Вот решение, которое
NS
типыO(n)
источник
здесь я сделал O (n) решение для объектов. Решение не в несколько строк, но ...
источник
Set
с пользовательскимDistinctWrapper
, вы должны использоватьDictionary
от DifferentAttributes для объектов. Когда вы будете следовать этой логике, вы в конечном итоге будете реализовывать [Dictionary.init(_:uniquingKeysWith:)
] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… , который теперь встроен в стандартную библиотеку. Проверьте, насколько это просто pastebin.com/w90pVe0pЯ использовал ответ @ Jean-Philippe Pellet и сделал расширение Array, которое выполняет операции над массивами, аналогичные множествам, сохраняя порядок элементов.
источник
Bool
, когда единственное значение, которое вы используетеtrue
. Вы достигаете "тип единицы" (тип только с одним возможным значением). Тип модуля Свифта - этоVoid
единственное значение()
(или пустой кортеж). Так что вы можете просто использовать[T: Void]
. Хотя вы не должны этого делать, потому что вы в основном только что изобрелиSet
. ИспользуйтеSet
вместо этого. См. Stackoverflow.com/a/55684308/3141234Это просто очень простая и удобная реализация. Вычисляемое свойство в расширении массива, которое имеет экватируемые элементы.
источник
O(n^2)
.Использование:
источник
O(n²)
.Готово....
пример
вывод массива без дубликатов - [1,2,4,6,8]
источник
Слегка укороченная версия, основанная на ответе расширения массива @ Jean-Philippe Pellet:
источник
insert
возвращает кортеж, который сообщает вам, был ли элемент уже там или был добавлен в первый раз. stackoverflow.com/a/55684308/3141234 Пожалуйста, удалите этот ответ.Вы всегда можете использовать словарь, потому что словарь может содержать только уникальные значения. Например:
Как видите, результирующий массив не всегда будет в «порядке». Если вы хотите отсортировать / заказать массив, добавьте это:
,
источник
Самый простой способ - использовать NSOrderedSet, который хранит уникальные элементы и сохраняет порядок элементов. Подобно:
источник