如何按Swift中的数组元素进行分组

Rub*_*ben 72 arrays nsarray ios 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] = []
Run Code Online (Sandbox Code Playgroud)

我可以手动调用下一个函数,以便有两个按"同名"分组的数组.

filteredArray1 = currentStat.statEvents.filter({$0.name == "dinner"})
filteredArray2 = currentStat.statEvents.filter({$0.name == "lunch"})
Run Code Online (Sandbox Code Playgroud)

问题是我不知道变量值,在这种情况下是"晚餐"和"午餐",所以我想按名称自动对这个statEvents数组进行分组,所以我得到的数量与名称不同的数组一样多.

我怎么能这样做?

ois*_*sdk 150

斯威夫特4:

自Swift 4以来,此功能已添加到标准库中.您可以像这样使用它:

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)
]
Run Code Online (Sandbox Code Playgroud)

斯威夫特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
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,append上面的函数复制了底层数组,而不是将其变异,这是更好的选择.这导致了相当大的减速.您可以使用引用类型包装器来解决此问题:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

即使您遍历最终字典两次,在大多数情况下,此版本仍然比原始字典更快.

斯威夫特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
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您可以将返回的"键" keyFunc作为名称:

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)
  ]
]
Run Code Online (Sandbox Code Playgroud)

因此,您将获得一个字典,其中每个键都是一个名称,每个值都是具有该名称的StatEvents数组.

斯威夫特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 }
Run Code Online (Sandbox Code Playgroud)

这给出了输出:

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
  ]
]
Run Code Online (Sandbox Code Playgroud)

(swiftstub在这里)

  • Swift 5.2 `字典(分组:currentStat.statEvents,按:\.name)` (2认同)

Ima*_*tit 51

使用Swift 4,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
Run Code Online (Sandbox Code Playgroud)

创建一个新的字典,其中键是给定闭包返回的分组,值是返回每个特定键的元素的数组.


以下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) { $0.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]
]
*/
Run Code Online (Sandbox Code Playgroud)

  • 好的,你还可以包括它也可以写成`let dictionary = Dictionary(grouping:statEvents){$ 0.name}` - Syntax Sugar coating (4认同)
  • @user1046037 Swift 5.2 `字典(分组:statEvents,按:\.name)` (3认同)

小智 30

Swift 4:你可以使用来自 apple开发者网站的init(分组:by :)

示例:

let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by: { $0.first! })
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下

   let dictionary = Dictionary(grouping: currentStat.statEvents, by:  { $0.name! })
Run Code Online (Sandbox Code Playgroud)


mie*_*tus 26

对于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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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)
  ]
]
Run Code Online (Sandbox Code Playgroud)

  • 一个用法示例将非常感谢:)谢谢! (9认同)

dua*_*uan 5

在Swift 4中,此扩展程序具有最佳性能,可帮助您的运营商实现链接

extension Sequence {
    func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
        return Dictionary.init(grouping: self, by: key)
    }
}
Run Code Online (Sandbox Code Playgroud)

例:

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 })
Run Code Online (Sandbox Code Playgroud)

创建:

[
    "ETH": [
        Asset(coin: "ETH", amount: 15)
    ],
    "BTC": [
        Asset(coin: "BTC", amount: 12),
        Asset(coin: "BTC", amount: 30)
    ]
]
Run Code Online (Sandbox Code Playgroud)