Saat ini di Swift Anda cukup mengetik Set( yourArray )
untuk membuat array unik. (Atau set memerintahkan jika diperlukan.)
Sebelum itu mungkin, bagaimana hal itu dilakukan?
Saya mungkin memiliki array yang terlihat seperti berikut:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
Atau, sungguh, setiap urutan bagian data yang diketik seperti. Yang ingin saya lakukan adalah memastikan bahwa hanya ada satu dari setiap elemen yang identik. Misalnya, array di atas akan menjadi:
[1, 4, 2, 6, 24, 15, 60]
Perhatikan bahwa duplikat 2, 6, dan 15 telah dihapus untuk memastikan bahwa hanya ada satu dari setiap elemen yang identik. Apakah Swift menyediakan cara untuk melakukan ini dengan mudah, atau apakah saya harus melakukannya sendiri?
arrays
swift
standard-library
Altair357
sumber
sumber
NSSet
, NSSet adalah kumpulan objek yang tidak terurut, jika perlu menjaga ketertiban NSOrderedSet.$.uniq(array)
github.com/ankurp/Dollar#uniq---uniqSet
dari Swift? Anda dapat memberikan daftar elemen yang tidak diurut dan unik.Jawaban:
Anda dapat menggulung sendiri, misalnya seperti ini ( diperbarui untuk Swift 1.2 dengan Set ):
Versi Swift 3:
Dan sebagai perpanjangan untuk
Array
:sumber
var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
updateValue(true, forKey: $0)...
alih-alihaddedDict(true, forKey: $0)...
return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }
seperti yang Anda katakan.let uniques = Array(Set(vals))
Anda dapat mengonversi ke set dan kembali ke array dengan mudah:
Ini tidak dijamin untuk mempertahankan urutan asli array.
sumber
originals
bukanHashable
; hanyaHashable
tipe data yang dapat ditambahkan ke Set, namun tipe data apa pun dapat ditambahkan ke array.Banyak jawaban tersedia di sini, tetapi saya melewatkan ekstensi sederhana ini, cocok untuk Swift 2 dan lebih tinggi:
Jadikan super sederhana. Dapat disebut seperti ini:
Penyaringan berdasarkan properti
Untuk memfilter array berdasarkan properti, Anda dapat menggunakan metode ini:
Yang dapat Anda hubungi sebagai berikut:
sumber
extension Array where Element: Equatable
) sedang digantikan oleh stackoverflow.com/a/36048862/1033581 yang menawarkan solusi yang lebih kuat (extension Sequence where Iterator.Element: Equatable
).O(n²)
kinerja waktu, yang sangat buruk untuk array besar.O(n²)
kompleksitas yang mengerikan ini kembali keO(n)
Swift 3.0
sumber
array
bukanHashable
; hanyaHashable
tipe data yang dapat ditambahkan ke Set, namun tipe data apa pun dapat ditambahkan ke array.Jika Anda memasukkan kedua ekstensi dalam kode Anda,
Hashable
versi yang lebih cepat akan digunakan bila memungkinkan, danEquatable
versi tersebut akan digunakan sebagai cadangan.Jika pesanan tidak penting, maka Anda selalu dapat menggunakan Set initializer ini .
sumber
O(n²)
kinerja waktu, yang sangat buruk untuk array besar.edit / perbarui Swift 4 atau lebih baru
Kami juga dapat memperluas
RangeReplaceableCollection
protokol untuk memungkinkannya digunakan denganStringProtocol
tipe juga:Metode bermutasi:
Untuk Swift 3 klik di sini
sumber
reduce
implementasinya, jadi sekarang kompleksitasnya berbeda.O(n)
demi sedikit), di mana versi berbasis-flat membutuhkan waktu 7,47x lebih lama untuk 8 juta entri unik dari 1 juta, menunjukkan bahwa versi berbasis-flatmap memiliki skala yang lebih baik . Entah bagaimana versi berbasis flatmap sedikit lebih baik daripadaO(n)
waktu!Cepat 4
setiap usaha untuk
insert
juga akan kembali tupel:(inserted: Bool, memberAfterInsert: Set.Element)
. Lihat dokumentasi .Menggunakan nilai yang dikembalikan membantu kita untuk menghindari perulangan atau melakukan operasi lainnya.
sumber
O(n^2)
, dan tidak ada yang memperhatikan.Cepat 4
Dijamin tetap memesan.
sumber
reduce
: keseluruhan, itu hanya satu baris lebih di seluruh proyek Anda untuk menulis fungsi sebagai:var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique
. Saya akui saya belum menguji kinerja relatif.O(n²)
kinerja waktu, yang sangat buruk untuk array besar.O(n²)
. Tidak ada yang cepat tentang ini.reduce
ataureduce(into:)
tidak akan membuat perbedaan kritis. Menulis ulang ini untuk tidak berulang kali meneleponcontains
akan membuat perbedaan JAUH lebih besar.Berikut adalah kategori
SequenceType
yang mempertahankan urutan asli array, tetapi menggunakan aSet
untuk melakukancontains
pencarian untuk menghindariO(n)
biaya padacontains(_:)
metode Array .Jika Anda tidak Hashable atau Equatable, Anda dapat memberikan predikat untuk melakukan pemeriksaan kesetaraan:
Sekarang, jika Anda tidak memiliki Hashable, tapi yang equatable, Anda dapat menggunakan metode ini:
Akhirnya, Anda dapat menambahkan versi jalur kunci unik seperti ini:
Anda dapat memasukkan keduanya ke dalam aplikasi Anda, Swift akan memilih yang tepat tergantung pada
Iterator.Element
jenis urutan Anda .sumber
O(n)
solusi. Anda dapat menggabungkan set operasi "centang" dan "masukkan" menjadi satu. Lihat stackoverflow.com/a/46354989/3141234Terinspirasi oleh https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift , kita dapat mendeklarasikan alat yang lebih kuat yang mampu memfilter unicity pada keyPath apa pun. Berkat komentar Alexander tentang berbagai jawaban mengenai kompleksitas, solusi di bawah ini harus mendekati optimal.
Solusi non-mutasi
Kami memperluas dengan fungsi yang dapat memfilter untuk unicity pada keyPath apa pun:
Catatan: dalam kasus di mana objek Anda tidak sesuai dengan RangeReplaceableCollection, tetapi tidak sesuai dengan Sequence, Anda dapat memiliki ekstensi tambahan ini, tetapi tipe pengembalian akan selalu menjadi Array:
Pemakaian
Jika kita menginginkan kesatuan untuk elemen itu sendiri, seperti dalam pertanyaan, kita menggunakan keyPath
\.self
:Jika kita menginginkan unicity untuk sesuatu yang lain (seperti untuk
id
koleksi benda) maka kita menggunakan keyPath pilihan kita:Solusi bermutasi
Kami memperluas dengan fungsi bermutasi yang dapat memfilter untuk unicity pada keyPath apa pun:
Pemakaian
Jika kita menginginkan kesatuan untuk elemen itu sendiri, seperti dalam pertanyaan, kita menggunakan keyPath
\.self
:Jika kita menginginkan unicity untuk sesuatu yang lain (seperti untuk
id
koleksi benda) maka kita menggunakan keyPath pilihan kita:sumber
keyPath
default\.self
, karena itu mungkin sebagian besar kasus penggunaan.Element
selalu membuatnyaHashable
. Alternatif untuk nilai default adalah menambahkan overload sederhana tanpa parameter:extension Sequence where Element: Hashable { func unique() { ... } }
Solusi alternatif (jika tidak optimal) dari sini menggunakan tipe yang tidak berubah daripada variabel:
Termasuk kontras dengan pendekatan imperatif Jean-Pillippe dengan pendekatan fungsional.
Sebagai bonus, fungsi ini berfungsi dengan string dan juga array!
Sunting: Jawaban ini ditulis pada tahun 2014 untuk Swift 1.0 (sebelumnya
Set
tersedia di Swift). Itu tidak memerlukan kepatuhan Hashable & berjalan dalam waktu kuadratik.sumber
contains
dan menambahkan array berjalan di O (n). Meskipun itu memiliki manfaat hanya membutuhkan yang setara, bukan hashable.filter
. Ini O (n ^ 2) (yang diperlukan jika Anda tidak ingin membutuhkanHashable
kesesuaian), tetapi Anda setidaknya harus menyebutkannya secara eksplisitcepat 2
dengan jawaban fungsi uniq :
menggunakan:
sumber
Bool
Nilai jelas berlebihan, sebagai kode Anda tidak pernah membacanya. Gunakan aSet
daripada aDictionary
dan Anda mendapatkan upvote saya.Dalam Swift 5
Output Akan
sumber
Satu lagi solusi Swift 3.0 untuk menghapus duplikat dari array. Solusi ini meningkat pada banyak solusi lain yang telah diusulkan oleh:
Diberikan bilangan bulat integer:
Kode fungsional:
Kode ekstensi array:
Kode ini mengambil keuntungan dari hasil yang dikembalikan oleh
insert
operasi aktifSet
, yang dijalankan padaO(1)
, dan mengembalikan tupel yang menunjukkan apakah item telah dimasukkan atau jika sudah ada dalam set.Jika item dalam set,
filter
akan mengecualikannya dari hasil akhir.sumber
defer
kode akan melakukan operasi uji set dua kali, satu dengancontains
dan satu denganinsert
. Selanjutnya membaca dokumentasi Swift, saya menemukan bahwainsert
mengembalikan tuple yang menunjukkan apakah elemen itu dimasukkan atau tidak, jadi saya telah menyederhanakan kode menghapuscontains
centang.extension Sequence where Iterator.Element: Hashable { ... }
insert
dancontains
memilikiO(1)
kompleksitas.O(1) + O(1) = O(1)
. Kedua operasi ini kemudian dilakukann
kali (sekali per panggilan penutupan dilewatifilter
, yang disebut sekali per elemen) Yaitu jika operasi membutuhkan jumlah waktu yang konstan terlepas dari ukuran input, kemudian melakukannya dua kali masih membuatnya membutuhkan waktu yang konstan itu terlepas dari ukuran input. Kompleksitas totalnya adalahO(n)
.Swift 4.x:
pemakaian:
atau
sumber
O(n^2)
. Jangan lakukan ini.Cepat 5
sumber
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
, ketika satu-satunya nilai yang Anda gunakan adalahtrue
. Anda meraih "tipe unit" (tipe dengan hanya satu nilai yang memungkinkan). Jenis unit Swift adalahVoid
, yang nilainya hanya()
(alias tuple kosong). Jadi Anda bisa menggunakannya[T: Void]
. Meskipun Anda seharusnya tidak melakukan itu, karena pada dasarnya Anda baru saja menemukanSet
. GunakanSet
sebagai gantinya. Lihat stackoverflow.com/a/55684308/3141234 Harap hapus jawaban ini.Berpikir seperti programmer fungsional :)
Untuk memfilter daftar berdasarkan pada apakah elemen telah terjadi, Anda perlu indeks. Anda dapat menggunakan
enumerated
untuk mendapatkan indeks danmap
kembali ke daftar nilai.Ini menjamin pesanan. Jika Anda tidak keberatan dengan urutannya maka jawaban yang ada
Array(Set(myArray))
lebih sederhana dan mungkin lebih efisien.PEMBARUAN: Beberapa catatan tentang efisiensi dan kebenaran
Beberapa orang berkomentar tentang efisiensi. Saya pasti di sekolah menulis kode yang benar dan sederhana pertama dan kemudian mencari tahu kemacetan kemudian, meskipun saya menghargai itu masih bisa diperdebatkan apakah ini lebih jelas daripada
Array(Set(array))
.Metode ini jauh lebih lambat daripada
Array(Set(array))
. Seperti yang disebutkan dalam komentar, itu menjaga ketertiban dan bekerja pada elemen yang tidak Hashable.Namun, metode @Alain T juga menjaga ketertiban dan juga jauh lebih cepat. Jadi kecuali tipe elemen Anda tidak dapat di hashable, atau Anda hanya perlu satu liner cepat, maka saya sarankan menggunakan solusi mereka.
Berikut adalah beberapa tes pada MacBook Pro (2014) pada Xcode 11.3.1 (Swift 5.1) dalam mode Rilis.
Fungsi profiler dan dua metode untuk membandingkan:
Dan sejumlah kecil input uji:
Memberikan sebagai output:
sumber
Array(Set(myArray))
, ini bekerja untuk hal-hal yang tidakHashable
Array(Set(myArray))
urutan array Anda dipertahankan.lastIndex(of:)
. Saya benar-benar tidak setuju atas kejelasan vs titik optimasi dalam kasus ini. Saya tidak berpikir implementasi ini sangat jelas, terutama dibandingkan dengan solusi berbasis set sederhana. Bagaimanapun, kode tersebut harus diekstraksi ke fungsi ekstensi. Algoritma ini pada dasarnya tidak dapat digunakan bahkan pada ukuran input rendah, seperti pada ribuan hingga puluhan ribu. Tidak sulit untuk menemukan kumpulan data seperti itu, orang dapat memiliki ribuan lagu, file, kontak, dll.Untuk array yang elemennya tidak bisa Hashable atau Sebanding (mis. Objek kompleks, kamus atau struct), ekstensi ini menyediakan cara umum untuk menghapus duplikat:
Anda tidak perlu repot membuat nilai Hashable dan memungkinkan Anda menggunakan kombinasi bidang yang berbeda untuk keunikan.
Catatan: untuk pendekatan yang lebih kuat, silakan lihat solusi yang diusulkan oleh Coeur di komentar di bawah.
stackoverflow.com/a/55684308/1033581
[EDIT] Alternatif 4 cepat
Dengan Swift 4.2 Anda dapat menggunakan kelas Hasher untuk membangun hash lebih mudah. Ekstensi di atas dapat diubah untuk memanfaatkan ini:
Sintaks pemanggilan sedikit berbeda karena closure menerima parameter tambahan yang berisi fungsi untuk meng-hash sejumlah variabel nilai (yang harus Hashable secara individual)
Ini juga akan bekerja dengan nilai keunikan tunggal (menggunakan $ 1 dan mengabaikan $ 0).
sumber
"\()"
, karena mungkin tidak memberi Anda nilai-nilai unik seperti sesuai dengan yangHashable
seharusnya. Contoh, jika elemen Anda sesuaiPrintable
dengan semua yang mengembalikan yang samadescription
, maka pemfilteran Anda gagal.T
untuk menjadiHashable
.Anda bisa menggunakan langsung kumpulan koleksi untuk menghapus duplikat, lalu melemparkannya kembali ke sebuah array
Kemudian Anda dapat memesan array seperti yang Anda inginkan
sumber
Versi sintaks yang sedikit lebih ringkas dari jawaban Swift 2 Daniel Krom , menggunakan trailing closure dan nama argumen steno, yang tampaknya didasarkan pada jawaban asli Airspeed Velocity :
Contoh penerapan tipe khusus yang dapat digunakan dengan
uniq(_:)
(yang harus sesuai denganHashable
, dan karenanyaEquatable
, karenaHashable
meluasEquatable
):Dalam kode di atas ...
id
, seperti yang digunakan dalam overload==
, bisa berupaEquatable
tipe apa pun (atau metode yang mengembalikanEquatable
tipe, misalnya,someMethodThatReturnsAnEquatableType()
). Kode yang dikomentari menunjukkan perpanjangan pemeriksaan untuk kesetaraan, di manasomeOtherEquatableProperty
properti lain dari suatuEquatable
tipe (tetapi juga bisa menjadi metode yang mengembalikanEquatable
tipe).id
, seperti yang digunakan dalamhashValue
properti yang dihitung (diharuskan untuk menyesuaikan diriHashable
), bisa berupa properti apa punHashable
(dan dengan demikianEquatable
) (atau metode yang mengembalikan suatuHashable
tipe).Contoh menggunakan
uniq(_:)
:sumber
Bool
, ketika satu-satunya nilai yang Anda gunakan adalahtrue
. Anda meraih "tipe unit" (tipe dengan hanya satu nilai yang memungkinkan). Jenis unit Swift adalahVoid
, yang nilainya hanya()
(alias tuple kosong). Jadi Anda bisa menggunakannya[T: Void]
. Meskipun Anda seharusnya tidak melakukan itu, karena pada dasarnya Anda baru saja menemukanSet
. GunakanSet
sebagai gantinya. Lihat stackoverflow.com/a/55684308/3141234Jika Anda membutuhkan nilai yang diurutkan, ini berfungsi (Swift 4)
let sortedValues = Array(Set(array)).sorted()
sumber
.sorted()
tujuan akhirnya. Salam.[2, 1, 1]
? Itu akan keluar[1, 2]
, itu tidak dipesan: p[2, 1, 1]
. Penampilan pertama elemen unik, secara berurutan[2, 1]
. Itu jawaban yang benar. Tetapi menggunakan (salah) Anda algoritma, Anda mendapatkan[1, 2]
, yang adalah diurutkan, tapi tidak dalam yang benar, asli, order.array
bukanHashable
; hanyaHashable
tipe data yang dapat ditambahkan ke Set, namun tipe data apa pun dapat ditambahkan ke array.Ini solusinya
NS
jenis warisanO(n)
sumber
di sini saya telah melakukan beberapa solusi O (n) untuk objek. Bukan solusi beberapa baris, tapi ...
sumber
Set
dengan kustomDistinctWrapper
, Anda harus menggunakanDictionary
dari differentAttributes ke objek. Ketika Anda mengikuti logika itu sampai tuntas, Anda akhirnya akan mengimplementasikan [Dictionary.init(_:uniquingKeysWith:)
] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… , yang sekarang dibangun di perpustakaan standar. Lihat betapa sederhananya ini pastebin.com/w90pVe0pSaya menggunakan jawaban @ Jean-Philippe Pellet dan membuat ekstensi Array yang melakukan operasi seperti set pada array, sambil mempertahankan urutan elemen.
sumber
Bool
, ketika satu-satunya nilai yang Anda gunakan adalahtrue
. Anda meraih "tipe unit" (tipe dengan hanya satu nilai yang memungkinkan). Jenis unit Swift adalahVoid
, yang nilainya hanya()
(alias tuple kosong). Jadi Anda bisa menggunakannya[T: Void]
. Meskipun Anda seharusnya tidak melakukan itu, karena pada dasarnya Anda baru saja menemukanSet
. GunakanSet
sebagai gantinya. Lihat stackoverflow.com/a/55684308/3141234Ini hanya implementasi yang sangat sederhana dan nyaman. Properti yang dihitung dalam perluasan Array yang memiliki elemen yang setara.
sumber
O(n^2)
.Pemakaian:
sumber
O(n²)
.Selesai ....
Contoh
output dari arrayWithoutDuplicates - [1,2,4,6,8]
sumber
Versi singkat yang singkat berdasarkan pada jawaban ekstensi array @ Jean-Philippe Pellet:
sumber
insert
mengembalikan tupel yang memberi tahu Anda apakah elemen sudah ada di sana, atau ditambahkan untuk pertama kalinya. stackoverflow.com/a/55684308/3141234 Harap hapus jawaban ini.Anda selalu dapat menggunakan Kamus, karena Kamus hanya dapat menyimpan nilai unik. Sebagai contoh:
Seperti yang Anda lihat, array yang dihasilkan tidak selalu berada di 'urutan'. Jika Anda ingin mengurutkan / memesan Array, tambahkan ini:
.
sumber
Cara termudah adalah dengan menggunakan NSOrderedSet, yang menyimpan elemen unik dan mempertahankan urutan elemen. Suka:
sumber