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개의 어레이를 "동일한 이름"으로 그룹화하기 위해 다음 기능을 수동으로 몇 번이나 호출할 수 있습니다.
filteredArray1 = currentStat.statEvents.filter({$0.name == "dinner"})
filteredArray2 = currentStat.statEvents.filter({$0.name == "lunch"})
문제는 변수 값(이 경우 "dinner" 및 "lunch")을 알 수 없기 때문에 statEvents 배열을 이름별로 자동으로 그룹화하여 이름이 달라지는 만큼의 배열을 얻는다는 것입니다.
내가 어떻게 그럴 수 있을까?
스위프트 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)
]
스위프트 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
위의 함수는 기본 배열을 제자리에서 변환하는 대신 복사합니다. 이는 더 바람직합니다.이로 인해 속도가 상당히 느려집니다.참조 유형 래퍼를 사용하여 문제를 해결할 수 있습니다.
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
}
}
최종 사전을 두 번 순회하더라도 이 버전은 대부분의 경우 원본보다 더 빠릅니다.
스위프트 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
이름은 다음과 같습니다.
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)
]
]
모든 키가 이름이고 모든 값이 해당 이름의 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 }
출력은 다음과 같습니다.
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
]
]
(스위프트 스터브는 여기에 있습니다)
스위프트 5와 함께라면,Dictionary
에는 이라는 이니셜라이저 메서드가 있습니다.init(grouping:by:)
에는 다음과 같은 선언이 있습니다.
init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S : Sequence
키가 지정된 폐쇄에서 반환된 그룹이고 값이 각 특정 키를 반환한 요소의 배열인 새 사전을 만듭니다.
다음 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
//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]
]
*/
Swift 4: 애플 개발자 사이트에서 init(그룹화: 기준)를 사용할 수 있습니다.
예:
let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by: { $0.first! })
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]
그래서 당신의 경우에는
let dictionary = Dictionary(grouping: currentStat.statEvents, by: { $0.name! })
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 { $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)
]
]
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: { $0.coin })
작성:
[
"ETH": [
Asset(coin: "ETH", amount: 15)
],
"BTC": [
Asset(coin: "BTC", amount: 12),
Asset(coin: "BTC", amount: 30)
]
]
다음을 기준으로 그룹화할 수도 있습니다.KeyPath
다음과 같이:
public extension Sequence {
func group<Key>(by keyPath: KeyPath<Element, Key>) -> [Key: [Element]] where Key: Hashable {
return Dictionary(grouping: self, by: {
$0[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)
]
]
스위프트 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)})
순서가 지정된 그룹화를 허용하도록 승인된 답변에 연장:
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]! }
}
}
그러면 모든 튜플에서 작동합니다.
let a = [(grouping: 10, content: "a"),
(grouping: 20, content: "b"),
(grouping: 10, content: "c")]
print(a.group { $0.grouping })
모든 구조체 또는 클래스와 함께:
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 })
이봐요, 해시 사전 대신 요소를 그룹화하는 동안 순서를 유지해야 한다면 튜플을 사용했고 그룹화하는 동안 목록의 순서를 유지했습니다.
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: { $0.0 == groupKey }) else { groupCategorized.append((groupKey, [item])); continue }
groupCategorized[index].1.append(item)
}
return groupCategorized
}
}
다음은 Swift 4 KeyPath를 그룹 비교기로 사용하면서 질서를 유지하기 위한 나의 튜플 기반 접근 방식입니다.
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
}
}
}
사용 방법의 예:
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)
Thr 사전(그룹화: 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
}
축소를 사용할 수 있습니다.
let result = currentStat.statEvents.reduce([String:[StatEvents]](), {
var previous = $0
previous[$1.name] = (previous[$1.name] ?? []) + [$1]
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
아니면 당신은 그것을 바꿀 수 있습니다.
let result = currentStat.statEvents.reduce(["lunch":[StatEvents](), "dinner":[StatEvents]()], {
var previous = $0
if let array = previous[$1.name] {
previous[$1.name] = array + [$1]
}
return previous
})
let filteredArray1 = result["lunch"]
let filteredArray2 = result["dinner"]
점심과 저녁만 찾는 것으로 제한하다
나의 방법이다.
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 { $0 }
}
}
이를 바탕으로 다음을 수행했습니다.
func filterItems() -> Dictionary<Int, [YourDataType]> {
return Dictionary(grouping: yourDataTypeVar, by: { (element: YourDataType) in
return item.name
})
}
사용할 수 있습니다.OrderedDictionary
https://github.com/apple/swift-collections 에서 주문한 키로 사전을 만듭니다.
import OrderedCollections
OrderedDictionary(grouping: statEvents, by: \.name)
또는Dictionary
순서가 지정되지 않은 키로 사전을 만듭니다.
Dictionary(grouping: statEvents, by: \.name)
"oisdk"의 예를 본떠서.클래스 이름 Demo & Source Code 링크를 기반으로 솔루션을 그룹 개체로 확장합니다.
클래스 이름을 기준으로 그룹화하기 위한 코드 조각:
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)
언급URL : https://stackoverflow.com/questions/31220002/how-to-group-by-the-elements-of-an-array-in-swift
'sourcetip' 카테고리의 다른 글
PHP에서 mysql 데이터베이스로 Excel 파일을 가져오는 방법 (0) | 2023.06.22 |
---|---|
기본 인수 값을 포함한 함수의 서명을 읽으려면 어떻게 해야 합니까? (0) | 2023.06.22 |
Vuex 작업 - Axios 반환 오류 (0) | 2023.06.22 |
data.frame을 사용해야 합니까, 아니면 matrix를 사용해야 합니까? (0) | 2023.06.22 |
Excel에서 날짜를 나타내는 숫자를 Java Date 객체로 변환 (0) | 2023.06.22 |