Katakanlah saya memiliki kode ini:
class Stat {
var statEvents : [StatEvents] = []
}
struct StatEvents {
var name: String
var date: String
var hours: Int
}
var currentStat = Stat()
currentStat.statEvents = [
StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
StatEvents(name: "dinner", date: "01-01-2015", hours: 1)
]
var filteredArray1 : [StatEvents] = []
var filteredArray2 : [StatEvents] = []
Saya bisa memanggil sebanyak mungkin fungsi berikutnya secara manual agar memiliki 2 array yang dikelompokkan dengan "nama yang sama".
filteredArray1 = currentStat.statEvents.filter({$0.name == "dinner"})
filteredArray2 = currentStat.statEvents.filter({$0.name == "lunch"})
Masalahnya adalah saya tidak akan tahu nilai variabelnya, dalam hal ini "makan malam" dan "makan siang", jadi saya ingin mengelompokkan array statEvents ini secara otomatis berdasarkan nama, jadi saya mendapatkan array sebanyak namanya berbeda.
Bagaimana saya bisa melakukan itu?
Dictionary
init(grouping:by:)
penginisialisasi baru .Jawaban:
Cepat 4:
Sejak Swift 4, fungsionalitas ini telah ditambahkan ke pustaka standar . Anda dapat menggunakannya seperti ini:
Dictionary(grouping: statEvents, by: { $0.name }) [ "dinner": [ StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1) ], "lunch": [ StatEvents(name: "lunch", date: "01-01-2015", hours: 1), StatEvents(name: "lunch", date: "01-01-2015", hours: 1) ]
Cepat 3:
public extension Sequence { func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] { var categories: [U: [Iterator.Element]] = [:] for element in self { let key = key(element) if case nil = categories[key]?.append(element) { categories[key] = [element] } } return categories } }
Sayangnya,
append
fungsi di atas menyalin larik yang mendasarinya, alih-alih memutasinya di tempatnya, yang lebih disukai. Ini menyebabkan perlambatan yang cukup besar . Anda bisa mengatasi masalah ini dengan menggunakan pembungkus tipe referensi:class Box<A> { var value: A init(_ val: A) { self.value = val } } public extension Sequence { func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] { var categories: [U: Box<[Iterator.Element]>] = [:] for element in self { let key = key(element) if case nil = categories[key]?.value.append(element) { categories[key] = Box([element]) } } var result: [U: [Iterator.Element]] = Dictionary(minimumCapacity: categories.count) for (key,val) in categories { result[key] = val.value } return result } }
Meskipun Anda melintasi kamus terakhir dua kali, versi ini masih lebih cepat daripada aslinya dalam banyak kasus.
Cepat 2:
public extension SequenceType { /// Categorises elements of self into a dictionary, with the keys given by keyFunc func categorise<U : Hashable>(@noescape keyFunc: Generator.Element -> U) -> [U:[Generator.Element]] { var dict: [U:[Generator.Element]] = [:] for el in self { let key = keyFunc(el) if case nil = dict[key]?.append(el) { dict[key] = [el] } } return dict } }
Dalam kasus Anda, Anda dapat memiliki "kunci" yang dikembalikan dengan
keyFunc
nama:currentStat.statEvents.categorise { $0.name } [ dinner: [ StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1) ], lunch: [ StatEvents(name: "lunch", date: "01-01-2015", hours: 1), StatEvents(name: "lunch", date: "01-01-2015", hours: 1) ] ]
Jadi Anda akan mendapatkan kamus, di mana setiap kunci adalah nama, dan setiap nilai adalah larik StatEvents dengan nama itu.
Cepat 1
func categorise<S : SequenceType, U : Hashable>(seq: S, @noescape keyFunc: S.Generator.Element -> U) -> [U:[S.Generator.Element]] { var dict: [U:[S.Generator.Element]] = [:] for el in seq { let key = keyFunc(el) dict[key] = (dict[key] ?? []) + [el] } return dict } categorise(currentStat.statEvents) { $0.name }
Yang memberikan output:
extension StatEvents : Printable { var description: String { return "\(self.name): \(self.date)" } } print(categorise(currentStat.statEvents) { $0.name }) [ dinner: [ dinner: 01-01-2015, dinner: 01-01-2015, dinner: 01-01-2015 ], lunch: [ lunch: 01-01-2015, lunch: 01-01-2015 ] ]
(Swiftstub ada di sini )
sumber
dict[key]
, (dalam contoh pertama saya, itu adalahans["dinner"]
). Jika Anda menginginkan indeks dari tiga hal itu sendiri, itu akan menjadi sepertienumerate(ans["dinner"])
, atau, jika Anda ingin mengakses melalui indeks, Anda dapat melakukannya seperti:,ans["dinner"]?[0]
yang akan mengembalikan Anda elemen pertama dari array yang disimpan di bawahdinner
.if case
) tidak diperlukan, tetapi yang lebih penting, menambahkan ke yang disimpan dalam kamus dengandict[key]?.append)
menyebabkan salinan terjadi setiap saat. Lihat rosslebeau.com/2016/…Dengan Swift 5,
Dictionary
memiliki metode penginisialisasi yang disebutinit(grouping:by:)
.init(grouping:by:)
memiliki deklarasi berikut:init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S : Sequence
Kode Playground berikut menunjukkan cara menggunakan
init(grouping:by:)
untuk memecahkan masalah Anda:struct StatEvents: CustomStringConvertible { let name: String let date: String let hours: Int var description: String { return "Event: \(name) - \(date) - \(hours)" } } let statEvents = [ StatEvents(name: "lunch", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "lunch", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1) ] let dictionary = Dictionary(grouping: statEvents, by: { (element: StatEvents) in return element.name }) //let dictionary = Dictionary(grouping: statEvents) { $0.name } // also works //let dictionary = Dictionary(grouping: statEvents, by: \.name) // also works print(dictionary) /* prints: [ "dinner": [Event: dinner - 01-01-2015 - 1, Event: dinner - 01-01-2015 - 1], "lunch": [Event: lunch - 01-01-2015 - 1, Event: lunch - 01-01-2015 - 1] ] */
sumber
let dictionary = Dictionary(grouping: statEvents) { $0.name }
- Lapisan Sintaks GulaDictionary(grouping: statEvents, by: \.name)
Swift 4: Anda dapat menggunakan init (pengelompokan: menurut :) dari situs pengembang apple
Contoh :
let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"] let studentsByLetter = Dictionary(grouping: students, by: { $0.first! }) // ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
Jadi dalam kasus Anda
let dictionary = Dictionary(grouping: currentStat.statEvents, by: { $0.name! })
sumber
Untuk Swift 3:
public extension Sequence { func categorise<U : Hashable>(_ key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] { var dict: [U:[Iterator.Element]] = [:] for el in self { let key = key(el) if case nil = dict[key]?.append(el) { dict[key] = [el] } } return dict } }
Pemakaian:
currentStat.statEvents.categorise { $0.name } [ dinner: [ StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1), StatEvents(name: "dinner", date: "01-01-2015", hours: 1) ], lunch: [ StatEvents(name: "lunch", date: "01-01-2015", hours: 1), StatEvents(name: "lunch", date: "01-01-2015", hours: 1) ] ]
sumber
Di Swift 4, ekstensi ini memiliki kinerja terbaik dan membantu menghubungkan operator Anda
extension Sequence { func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] { return Dictionary.init(grouping: self, by: key) } }
Contoh:
struct Asset { let coin: String let amount: Int } let assets = [ Asset(coin: "BTC", amount: 12), Asset(coin: "ETH", amount: 15), Asset(coin: "BTC", amount: 30), ] let grouped = assets.group(by: { $0.coin })
menciptakan:
[ "ETH": [ Asset(coin: "ETH", amount: 15) ], "BTC": [ Asset(coin: "BTC", amount: 12), Asset(coin: "BTC", amount: 30) ] ]
sumber
assets.group(by: { $0.coin.uppercased() })
, tetapi 'lebih baik memetakannya daripada mengelompokkanAnda juga dapat mengelompokkan menurut
KeyPath
, seperti ini:public extension Sequence { func group<Key>(by keyPath: KeyPath<Element, Key>) -> [Key: [Element]] where Key: Hashable { return Dictionary(grouping: self, by: { $0[keyPath: keyPath] }) } }
Menggunakan contoh kripto @ duan:
struct Asset { let coin: String let amount: Int } let assets = [ Asset(coin: "BTC", amount: 12), Asset(coin: "ETH", amount: 15), Asset(coin: "BTC", amount: 30), ]
Kemudian penggunaannya terlihat seperti ini:
let grouped = assets.group(by: \.coin)
Menghasilkan hasil yang sama:
[ "ETH": [ Asset(coin: "ETH", amount: 15) ], "BTC": [ Asset(coin: "BTC", amount: 12), Asset(coin: "BTC", amount: 30) ] ]
sumber
func grouped<Key: Hashable>(by keyForValue: (Element) -> Key) -> [Key: [Element]] {
.init(grouping: self, by: keyForValue)
}
ini akan memungkinkan Anda untuk meneleponassets.grouped(by: \.coin)
atauassets.grouped { $0.coin }
Cepat 4
struct Foo { let fizz: String let buzz: Int } let foos: [Foo] = [Foo(fizz: "a", buzz: 1), Foo(fizz: "b", buzz: 2), Foo(fizz: "a", buzz: 3), ] // use foos.lazy.map instead of foos.map to avoid allocating an // intermediate Array. We assume the Dictionary simply needs the // mapped values and not an actual Array let foosByFizz: [String: Foo] = Dictionary(foos.lazy.map({ ($0.fizz, $0)}, uniquingKeysWith: { (lhs: Foo, rhs: Foo) in // Arbitrary business logic to pick a Foo from // two that have duplicate fizz-es return lhs.buzz > rhs.buzz ? lhs : rhs }) // We don't need a uniquing closure for buzz because we know our buzzes are unique let foosByBuzz: [String: Foo] = Dictionary(uniqueKeysWithValues: foos.lazy.map({ ($0.buzz, $0)})
sumber
Memperluas jawaban yang diterima untuk memungkinkan pengelompokan yang teratur :
extension Sequence { func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] { var groups: [GroupingType: [Iterator.Element]] = [:] var groupsOrder: [GroupingType] = [] forEach { element in let key = key(element) if case nil = groups[key]?.append(element) { groups[key] = [element] groupsOrder.append(key) } } return groupsOrder.map { groups[$0]! } } }
Kemudian ini akan berfungsi pada tupel apa pun :
let a = [(grouping: 10, content: "a"), (grouping: 20, content: "b"), (grouping: 10, content: "c")] print(a.group { $0.grouping })
Serta setiap struct atau kelas :
struct GroupInt { var grouping: Int var content: String } let b = [GroupInt(grouping: 10, content: "a"), GroupInt(grouping: 20, content: "b"), GroupInt(grouping: 10, content: "c")] print(b.group { $0.grouping })
sumber
Berikut adalah pendekatan berbasis tupel saya untuk menjaga ketertiban saat menggunakan Swift 4 KeyPath sebagai pembanding grup:
extension Sequence{ func group<T:Comparable>(by:KeyPath<Element,T>) -> [(key:T,values:[Element])]{ return self.reduce([]){(accumulator, element) in var accumulator = accumulator var result :(key:T,values:[Element]) = accumulator.first(where:{ $0.key == element[keyPath:by]}) ?? (key: element[keyPath:by], values:[]) result.values.append(element) if let index = accumulator.index(where: { $0.key == element[keyPath: by]}){ accumulator.remove(at: index) } accumulator.append(result) return accumulator } } }
Contoh cara menggunakannya:
struct Company{ let name : String let type : String } struct Employee{ let name : String let surname : String let company: Company } let employees : [Employee] = [...] let companies : [Company] = [...] employees.group(by: \Employee.company.type) // or employees.group(by: \Employee.surname) // or companies.group(by: \Company.type)
sumber
Hai jika Anda perlu menjaga ketertiban saat mengelompokkan elemen, bukan kamus hash, saya telah menggunakan tupel dan menjaga urutan daftar saat mengelompokkan.
extension Sequence { func zmGroup<U : Hashable>(by: (Element) -> U) -> [(U,[Element])] { var groupCategorized: [(U,[Element])] = [] for item in self { let groupKey = by(item) guard let index = groupCategorized.index(where: { $0.0 == groupKey }) else { groupCategorized.append((groupKey, [item])); continue } groupCategorized[index].1.append(item) } return groupCategorized } }
sumber
Kamus Thr (pengelompokan: arr) sangat mudah!
func groupArr(arr: [PendingCamera]) { let groupDic = Dictionary(grouping: arr) { (pendingCamera) -> DateComponents in print("group arr: \(String(describing: pendingCamera.date))") let date = Calendar.current.dateComponents([.day, .year, .month], from: (pendingCamera.date)!) return date } var cams = [[PendingCamera]]() groupDic.keys.forEach { (key) in print(key) let values = groupDic[key] print(values ?? "") cams.append(values ?? []) } print(" cams are \(cams)") self.groupdArr = cams }
sumber
Mengambil contoh dari "oisdk" . Memperluas solusi untuk mengelompokkan objek berdasarkan nama kelas Tautan Demo & Kode Sumber .
Potongan kode untuk pengelompokan berdasarkan Nama Kelas:
func categorise<S : SequenceType>(seq: S) -> [String:[S.Generator.Element]] { var dict: [String:[S.Generator.Element]] = [:] for el in seq { //Assigning Class Name as Key let key = String(el).componentsSeparatedByString(".").last! //Generating a dictionary based on key-- Class Names dict[key] = (dict[key] ?? []) + [el] } return dict } //Grouping the Objects in Array using categorise let categorised = categorise(currentStat) print("Grouped Array :: \(categorised)") //Key from the Array i.e, 0 here is Statt class type let key_Statt:String = String(currentStat.objectAtIndex(0)).componentsSeparatedByString(".").last! print("Search Key :: \(key_Statt)") //Accessing Grouped Object using above class type key let arr_Statt = categorised[key_Statt] print("Array Retrieved:: ",arr_Statt) print("Full Dump of Array::") dump(arr_Statt)
sumber