কীভাবে সুইফটে একটি অ্যারের উপাদান দ্বারা গোষ্ঠী করা যায়


90

বলি যে আমার এই কোডটি রয়েছে:

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"})

সমস্যাটি হ'ল আমি ভেরিয়েবলের মানটি জানব না, এই ক্ষেত্রে "ডিনার" এবং "মধ্যাহ্নভোজন", সুতরাং আমি স্ট্যাটেন্টসগুলির এই অ্যারেটি নাম অনুসারে স্বয়ংক্রিয়ভাবে গ্রুপ করতে চাই, তাই নামটি আলাদা হওয়ার সাথে সাথে আমি অনেকগুলি অ্যারে পাই।

আমি এটা কিভাবে করতে পারি?


সুইফট 4 এর জন্য আমার উত্তরটি দেখুন যা নতুন Dictionary init(grouping:by:)ইনিশিয়ালাইজার ব্যবহার করে ।
ইমানু পেটিট

উত্তর:


196

সুইফট 4:

সুইফট 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)
  ]
]

সুতরাং আপনি একটি অভিধান পাবেন, যেখানে প্রতিটি কী একটি নাম এবং প্রতিটি মান সেই নামের সাথে স্ট্যাটএভেন্টসের একটি অ্যারে।

সুইফট ঘ

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
  ]
]

(সুইফেসটবটি এখানে রয়েছে )


আপনাকে অনেক ধন্যবাদ @ আইসডকে! আপনি কি জানেন যে অভিধানটির তৈরি হওয়া মানগুলির সূচকটি অ্যাক্সেস করার কোনও উপায় আছে কিনা? আমি বলতে চাইছি কীগুলি এবং মানগুলি কীভাবে পেতে হয় তা আমি জানি তবে আমি সেই অভিধানগুলির "0", "1", "2" ... সূচকটি পেতে চাই
রুবেন

সুতরাং আপনি যদি চান, আপনার অভিধানে তিনটি "ডিনার" মানগুলি বলুন, আপনি যাবেন dict[key], (আমার প্রথম উদাহরণে এটি হবে ans["dinner"])। আপনি যদি তিনটি জিনিসের সূচকগুলি নিজেরাই চেয়েছিলেন তবে এটি এমন কিছু হবে enumerate(ans["dinner"])বা আপনি সূচকগুলির মাধ্যমে অ্যাক্সেস করতে চান তবে আপনি এটি পছন্দ করতে পারেন: ans["dinner"]?[0]যা আপনাকে আন্ডারে সঞ্চিত অ্যারের প্রথম উপাদানটি ফিরিয়ে দেবে dinner
oisdk

আপস এটি সর্বদা আমাকে শূন্য করে দেয়
রুবেন

ওহ হ্যাঁ আমি এটি পেয়েছি, তবে সমস্যাটি হ'ল এই উদাহরণে আমি "ডিনার" এর মূল্যটি জানার কথা বলেছিলাম, তবে আসল কোডে আমি এই মানগুলি জানব না এবং কতগুলি আইটেমের অভিধান থাকবে
রুবেন

4
এটি একটি সমাধানের দিকে ভাল শুরু, তবে এটির কয়েকটি সংক্ষিপ্ত আগমন রয়েছে। ( if case) এখানে প্যাটার্ন মেলানোর ব্যবহার অপ্রয়োজনীয়, তবে আরও গুরুত্বপূর্ণ, প্রতি অভিধানে কোনও অভিধানের মধ্যে কোনও সঞ্চিত স্টোরেজে সংযোজন করার dict[key]?.append)কারণ রয়েছে। Rosslebeau.com/2016/…
আলেকজান্ডার

65

সুইফট 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

একটি নতুন অভিধান তৈরি করে যেখানে কীগুলি প্রদত্ত ক্লোজারের দ্বারা ফিরে আসা গ্রুপিং এবং মানগুলি প্রতিটি নির্দিষ্ট কীকে ফেরত দেওয়া উপাদানগুলির অ্যারে হয়।


নীচের খেলার মাঠের কোডটি দেখায় যে কীভাবে 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]
]
*/

4
ভাল একটি, আপনি এটি অন্তর্ভুক্ত করতে পারেন যে এটি হিসাবে লেখা যেতে পারে let dictionary = Dictionary(grouping: statEvents) { $0.name }- সিনট্যাক্স চিনির আবরণ
ব্যবহারকারী 1046037

4
এটি উত্তর 4 টি শুরু হওয়া উচিত - সম্পূর্ণরূপে আপেল দ্বারা সমর্থিত, এবং আশা করা যায় যে আরও অভিনয় করবেন।
হারবাল 7

ভবিষ্যদ্বাণীতে ফিরে আসা নন-অপটিনাল কীটির দিকেও মনোযোগ দিন, অন্যথায় আপনি ত্রুটি দেখতে পাবেন: "আরও প্রসঙ্গ ছাড়াই অভিব্যক্তিটির ধরণের বিষয়টি দ্ব্যর্থক ..."
Asike

4
@ ব্যবহারকারী 1046037 সুইফ্ট 5.2Dictionary(grouping: statEvents, by: \.name)
লিও ডাবাস

31

সুইফট 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! })

4
এটি এতদূর সেরা উত্তর, এটি জানত না
এটির

এটি কী-প্যাথের সাথেও কাজ করে: অভিধান = অভিধান করুন (গ্রুপিং: কারেন্টস্ট্যাট.স্ট্যাট এভেস্টস, লিখেছেন: name। নাম)
জিম হাংস

26

সুইফট 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)
  ]
]

9
একটি ব্যবহারের উদাহরণটি অত্যন্ত প্রশংসা হবে :) ধন্যবাদ!
সেঞ্চুরিয়ান

ব্যবহারের উদাহরণ এখানে: আপনারআরআরএই.ক্যাটরেইজ (বর্তমান স্ট্যাট.স্ট্যাটএভেন্টস) {$ 0. নাম}} ফাংশনটি অভিধানটি <স্ট্রিং, অ্যারে <স্ট্যাটভেস্টস >>
সেঞ্চুরিয়ন

6

সুইফট 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)
    ]
]

আপনি ব্যবহারের উদাহরণ লিখতে পারেন?
উত্কু ডালমাজ

@ বিআনসি যেমন বিটিসি এবং বিটিসি'র মতো মামলা গণ্য করা উচিত তা উপেক্ষা করা সম্ভব ...
শিরাজী

4
@MoinShirazi assets.group(by: { $0.coin.uppercased() }), কিন্তু তার 'ভালো তারপর গ্রুপ ম্যাপ
Duan

3

আপনি গ্রুপ দ্বারাও পারেন 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]
        })
    }
}

@ ডুয়ান এর ক্রিপ্টো উদাহরণ ব্যবহার করে:

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)
    ]
]

আপনি কীপথের পরিবর্তে একটি প্রিকেট পাস করতে পারেন এটি func grouped<Key: Hashable>(by keyForValue: (Element) -> Key) -> [Key: [Element]] { .init(grouping: self, by: keyForValue) }আপনাকে কল করতে দেয় assets.grouped(by: \.coin)বাassets.grouped { $0.coin }
লিও ডাবাস

2

সুইফট 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)})

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 })

0

সুইফট 4 কীপ্যাথটিকে গ্রুপের তুলনাকারী হিসাবে ব্যবহার করার সময় শৃঙ্খলা রক্ষার জন্য আমার টিপল ভিত্তিক পদ্ধতিটি এখানে রয়েছে :

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)

0

আরে আপনার যদি হ্যাশ অভিধানের পরিবর্তে উপাদানগুলি গোছানোর সময় ক্রম বজায় রাখতে হয় তবে আমি টিপলস ব্যবহার করেছি এবং গ্রুপিংয়ের সময় তালিকার ক্রম রেখেছি।

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
   }
}

0

থার ডিকশনারি (গ্রুপিং: আরআর) এত সহজ!

 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
}

-2

"Oisdk" উদাহরণ থেকে একটি পাতা নেওয়া । শ্রেণীর নাম ডেমো এবং উত্স কোড লিঙ্কের উপর ভিত্তি করে গ্রুপ অবজেক্টগুলিতে সমাধান বাড়ানো ।

শ্রেণীর নামের উপর ভিত্তি করে গ্রুপিংয়ের জন্য কোড স্নিপেট:

 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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.