如何按 Swift 中的数组元素分组
How to group by the elements of an array in Swift
假设我有这个代码:
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] = []
为了让 2 个数组按 "same name" 分组,我可以多次手动调用下一个函数。
filteredArray1 = currentStat.statEvents.filter({[=12=].name == "dinner"})
filteredArray2 = currentStat.statEvents.filter({[=12=].name == "lunch"})
问题是我不知道变量值,在本例中为 "dinner" 和 "lunch",所以我想按名称自动对这个 statEvents 数组进行分组,所以我得到名称不同的数组数。
我该怎么做?
Swift 4:
自 Swift 4 起,此功能已 added to the standard library。你可以像这样使用它:
Dictionary(grouping: statEvents, by: { [=10=].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)
]
Swift 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
}
}
不幸的是,上面的 append
函数复制了底层数组,而不是就地改变它,这会更可取。 This causes a pretty big slowdown。您可以通过使用引用类型包装器来解决这个问题:
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
}
}
即使你遍历最终字典两次,这个版本在大多数情况下仍然比原来的更快。
Swift 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
}
}
在你的例子中,你可以让 keyFunc
返回的 "keys" 是名字:
currentStat.statEvents.categorise { [=14=].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)
]
]
所以你会得到一个字典,其中每个键都是一个名称,每个值都是具有该名称的 StatEvents 的数组。
Swift 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) { [=15=].name }
给出输出:
extension StatEvents : Printable {
var description: String {
return "\(self.name): \(self.date)"
}
}
print(categorise(currentStat.statEvents) { [=16=].name })
[
dinner: [
dinner: 01-01-2015,
dinner: 01-01-2015,
dinner: 01-01-2015
], lunch: [
lunch: 01-01-2015,
lunch: 01-01-2015
]
]
(swiftstub 是 here)
从 中摘下一片叶子。
将解决方案扩展到基于 class 名称 Demo & Source Code link.
的对象分组
根据 Class 姓名分组的代码段:
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)
对于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
}
}
用法:
currentStat.statEvents.categorise { [=11=].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)
]
]
扩展已接受的答案以允许 有序 分组:
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[[=10=]]! }
}
}
然后它将对任何元组起作用:
let a = [(grouping: 10, content: "a"),
(grouping: 20, content: "b"),
(grouping: 10, content: "c")]
print(a.group { [=11=].grouping })
以及任何 struct 或 class:
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 { [=12=].grouping })
对于 Swift 5,Dictionary
有一个名为 init(grouping:by:)
的初始化方法。 init(grouping:by:)
有以下声明:
init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S : Sequence
Creates a new dictionary where the keys are the groupings returned by the given closure and the values are arrays of the elements that returned each specific key.
以下 Playground 代码显示了如何使用 init(grouping:by:)
来解决您的问题:
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) { [=11=].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]
]
*/
嘿,如果你需要在分组元素而不是哈希字典时保持顺序,我使用了元组并在分组时保持列表的顺序。
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.firstIndex(where: { [=10=].0 == groupKey }) else { groupCategorized.append((groupKey, [item])); continue }
groupCategorized[index].1.append(item)
}
return groupCategorized
}
}
这是我在使用 Swift 4 KeyPath's 作为组比较器时保持顺序的基于元组的方法:
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:{ [=10=].key == element[keyPath:by]}) ?? (key: element[keyPath:by], values:[])
result.values.append(element)
if let index = accumulator.index(where: { [=10=].key == element[keyPath: by]}){
accumulator.remove(at: index)
}
accumulator.append(result)
return accumulator
}
}
}
使用示例:
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)
Swift 4: 你可以使用 init(grouping:by:) from apple developer site
示例:
let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by: { [=10=].first! })
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
所以在你的情况下
let dictionary = Dictionary(grouping: currentStat.statEvents, by: { [=11=].name! })
在 Swift 4 中,此扩展具有最佳性能并帮助链接您的运算符
extension Sequence {
func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
return Dictionary.init(grouping: self, by: key)
}
}
示例:
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: { [=11=].coin })
创建:
[
"ETH": [
Asset(coin: "ETH", amount: 15)
],
"BTC": [
Asset(coin: "BTC", amount: 12),
Asset(coin: "BTC", amount: 30)
]
]
Swift 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({ ([=10=].fizz, [=10=])},
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({ ([=10=].buzz, [=10=])})
Thr Dictionary(grouping: arr)就是这么简单!
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
}
您也可以按 KeyPath
分组,像这样:
public extension Sequence {
func group<Key>(by keyPath: KeyPath<Element, Key>) -> [Key: [Element]] where Key: Hashable {
return Dictionary(grouping: self, by: {
[=10=][keyPath: keyPath]
})
}
}
使用@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),
]
然后用法如下:
let grouped = assets.group(by: \.coin)
产生相同的结果:
[
"ETH": [
Asset(coin: "ETH", amount: 15)
],
"BTC": [
Asset(coin: "BTC", amount: 12),
Asset(coin: "BTC", amount: 30)
]
]
我的方式
extension Array {
func group<T: Hashable>(by key: (_ element: Element) -> T) -> [[Element]] {
var categories: [T: [Element]] = [:]
for element in self {
let key = key(element)
categories[key, default: []].append(element)
}
return categories.values.map { [=10=] }
}
}
你可以使用reduce
let result = currentStat.statEvents.reduce([String:[StatEvents]](), {
var previous = [=10=]
previous[.name] = (previous[.name] ?? []) + []
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
或者您可以将其更改为
let result = currentStat.statEvents.reduce(["lunch":[StatEvents](), "dinner":[StatEvents]()], {
var previous = [=11=]
if let array = previous[.name] {
previous[.name] = array + []
}
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
仅限于寻找午餐和晚餐
基于this,我这样做了:
func filterItems() -> Dictionary<Int, [YourDataType]> {
return Dictionary(grouping: yourDataTypeVar, by: { (element: YourDataType) in
return item.name
})
}
假设我有这个代码:
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] = []
为了让 2 个数组按 "same name" 分组,我可以多次手动调用下一个函数。
filteredArray1 = currentStat.statEvents.filter({[=12=].name == "dinner"})
filteredArray2 = currentStat.statEvents.filter({[=12=].name == "lunch"})
问题是我不知道变量值,在本例中为 "dinner" 和 "lunch",所以我想按名称自动对这个 statEvents 数组进行分组,所以我得到名称不同的数组数。
我该怎么做?
Swift 4:
自 Swift 4 起,此功能已 added to the standard library。你可以像这样使用它:
Dictionary(grouping: statEvents, by: { [=10=].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)
]
Swift 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
}
}
不幸的是,上面的 append
函数复制了底层数组,而不是就地改变它,这会更可取。 This causes a pretty big slowdown。您可以通过使用引用类型包装器来解决这个问题:
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
}
}
即使你遍历最终字典两次,这个版本在大多数情况下仍然比原来的更快。
Swift 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
}
}
在你的例子中,你可以让 keyFunc
返回的 "keys" 是名字:
currentStat.statEvents.categorise { [=14=].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)
]
]
所以你会得到一个字典,其中每个键都是一个名称,每个值都是具有该名称的 StatEvents 的数组。
Swift 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) { [=15=].name }
给出输出:
extension StatEvents : Printable {
var description: String {
return "\(self.name): \(self.date)"
}
}
print(categorise(currentStat.statEvents) { [=16=].name })
[
dinner: [
dinner: 01-01-2015,
dinner: 01-01-2015,
dinner: 01-01-2015
], lunch: [
lunch: 01-01-2015,
lunch: 01-01-2015
]
]
(swiftstub 是 here)
从
根据 Class 姓名分组的代码段:
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)
对于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
}
}
用法:
currentStat.statEvents.categorise { [=11=].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)
]
]
扩展已接受的答案以允许 有序 分组:
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[[=10=]]! }
}
}
然后它将对任何元组起作用:
let a = [(grouping: 10, content: "a"),
(grouping: 20, content: "b"),
(grouping: 10, content: "c")]
print(a.group { [=11=].grouping })
以及任何 struct 或 class:
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 { [=12=].grouping })
对于 Swift 5,Dictionary
有一个名为 init(grouping:by:)
的初始化方法。 init(grouping:by:)
有以下声明:
init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S : Sequence
Creates a new dictionary where the keys are the groupings returned by the given closure and the values are arrays of the elements that returned each specific key.
以下 Playground 代码显示了如何使用 init(grouping:by:)
来解决您的问题:
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) { [=11=].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]
]
*/
嘿,如果你需要在分组元素而不是哈希字典时保持顺序,我使用了元组并在分组时保持列表的顺序。
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.firstIndex(where: { [=10=].0 == groupKey }) else { groupCategorized.append((groupKey, [item])); continue }
groupCategorized[index].1.append(item)
}
return groupCategorized
}
}
这是我在使用 Swift 4 KeyPath's 作为组比较器时保持顺序的基于元组的方法:
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:{ [=10=].key == element[keyPath:by]}) ?? (key: element[keyPath:by], values:[])
result.values.append(element)
if let index = accumulator.index(where: { [=10=].key == element[keyPath: by]}){
accumulator.remove(at: index)
}
accumulator.append(result)
return accumulator
}
}
}
使用示例:
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)
Swift 4: 你可以使用 init(grouping:by:) from apple developer site
示例:
let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by: { [=10=].first! })
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
所以在你的情况下
let dictionary = Dictionary(grouping: currentStat.statEvents, by: { [=11=].name! })
在 Swift 4 中,此扩展具有最佳性能并帮助链接您的运算符
extension Sequence {
func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
return Dictionary.init(grouping: self, by: key)
}
}
示例:
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: { [=11=].coin })
创建:
[
"ETH": [
Asset(coin: "ETH", amount: 15)
],
"BTC": [
Asset(coin: "BTC", amount: 12),
Asset(coin: "BTC", amount: 30)
]
]
Swift 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({ ([=10=].fizz, [=10=])},
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({ ([=10=].buzz, [=10=])})
Thr Dictionary(grouping: arr)就是这么简单!
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
}
您也可以按 KeyPath
分组,像这样:
public extension Sequence {
func group<Key>(by keyPath: KeyPath<Element, Key>) -> [Key: [Element]] where Key: Hashable {
return Dictionary(grouping: self, by: {
[=10=][keyPath: keyPath]
})
}
}
使用@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),
]
然后用法如下:
let grouped = assets.group(by: \.coin)
产生相同的结果:
[
"ETH": [
Asset(coin: "ETH", amount: 15)
],
"BTC": [
Asset(coin: "BTC", amount: 12),
Asset(coin: "BTC", amount: 30)
]
]
我的方式
extension Array {
func group<T: Hashable>(by key: (_ element: Element) -> T) -> [[Element]] {
var categories: [T: [Element]] = [:]
for element in self {
let key = key(element)
categories[key, default: []].append(element)
}
return categories.values.map { [=10=] }
}
}
你可以使用reduce
let result = currentStat.statEvents.reduce([String:[StatEvents]](), {
var previous = [=10=]
previous[.name] = (previous[.name] ?? []) + []
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
或者您可以将其更改为
let result = currentStat.statEvents.reduce(["lunch":[StatEvents](), "dinner":[StatEvents]()], {
var previous = [=11=]
if let array = previous[.name] {
previous[.name] = array + []
}
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
仅限于寻找午餐和晚餐
基于this,我这样做了:
func filterItems() -> Dictionary<Int, [YourDataType]> {
return Dictionary(grouping: yourDataTypeVar, by: { (element: YourDataType) in
return item.name
})
}