সুইফটে অভিধানে অবজেক্টের অ্যারে ম্যাপ করুন


94

আমার Personঅবজেক্টগুলির একটি অ্যারে রয়েছে :

class Person {
   let name:String
   let position:Int
}

এবং অ্যারেটি হ'ল:

let myArray = [p1,p1,p3]

আমি ক্লাসিক সমাধানটির myArrayএকটি অভিধান হিসাবে মানচিত্র তৈরি করতে চাই [position:name]:

var myDictionary =  [Int:String]()

for person in myArray {
    myDictionary[person.position] = person.name
}

সেখানে সুইফট করে যে কোনো মার্জিত উপায় যে কাজ করতে কার্মিক পদ্ধতির সাথে আছেন map, flatMap... অথবা আধুনিক সুইফট শৈলী


গেমটি থেকে একটু দেরি হলেও আপনি কি এর অভিধান চান [position:name]নাকি [position:[name]]? যদি আপনার একই পদে দু'জন লোক থাকে তবে আপনার অভিধানটি কেবল আপনার লুপের মধ্যে থাকা শেষটিকেই রাখবে .... আমার একটি অনুরূপ প্রশ্ন রয়েছে যার জন্য আমি একটি সমাধান খুঁজতে চাইছি, তবে আমি চাই ফলাফলটি এর মতো হোক[1: [p1, p3], 2: [p2]]
Zonker.in.Geneva

4
এগুলি একটি বিল্ট ইন ফাংশন। এখানে দেখুন । এটি মূলতDictionary(uniqueKeysWithValues: array.map{ ($0.key, $0) })
হানি

উত্তর:


128

ঠিক আছে mapএটির একটি ভাল উদাহরণ নয়, কারণ এটি লুপিংয়ের মতো, আপনি reduceপরিবর্তে এটি ব্যবহার করতে পারেন , এটি আপনার প্রতিটি বস্তুকে একত্রিত করতে এবং একক মানে রূপান্তরিত করতে গ্রহণ করেছে:

let myDictionary = myArray.reduce([Int: String]()) { (dict, person) -> [Int: String] in
    var dict = dict
    dict[person.position] = person.name
    return dict
}

//[2: "b", 3: "c", 1: "a"]

সুইফট 4 বা উচ্চতরগুলিতে দয়া করে পরিষ্কার বাক্য গঠনগুলির জন্য নীচের উত্তরটি ব্যবহার করুন।


8
আমি এই পদ্ধতির পছন্দ করি তবে একটি অভিধান লোড করার জন্য লুপ তৈরি করার চেয়ে কি এটি আরও দক্ষ? আমি পারফরম্যান্স নিয়ে চিন্তিত কারণ এটি প্রতিটি আইটেমের মতো মনে হয় এটির থেকে আরও বড়
কোনও অভিধানের

এটি আসলে একটি লুপ, এটিকে এটি লেখার সহজ উপায়, পারফরম্যান্সটি সাধারণ লুপের চেয়ে একই বা একরকম ভাল
Tj3n

4
কেন্ডল, নীচে আমার উত্তরটি দেখুন, সুইফট ৪ ব্যবহার করার সময় এটি লুপিংয়ের চেয়ে দ্রুততর করতে পারে যদিও এটি নিশ্চিত করার জন্য আমি এটি বেঞ্চমার্ক করি নি।
21

4
এটি ব্যবহার করবেন না !!! @ কেন্ডালহেল্মস্টেটার জেলনার উল্লেখ করেছেন যে, এই পদ্ধতির সমস্যাটি প্রতিটি পুনরাবৃত্তি, এটি সম্পূর্ণ অভিধানটি বর্তমান অবস্থায় অনুলিপি করছে, সুতরাং প্রথম লুপে এটি একটি-আইটেমের অভিধানটি অনুলিপি করছে। দ্বিতীয় পুনরাবৃত্তি এটি একটি দ্বি-আইটেমের অভিধানটি অনুলিপি করছে। এটি একটি বিশাল পারফরম্যান্স ড্রেন !! এটি করার সর্বোত্তম উপায় হ'ল অভিধানে এমন একটি সুবিধাপূর্ণ সূচনা লিখুন যা আপনার অ্যারে নেয় এবং এতে সমস্ত আইটেম যুক্ত হয়। এইভাবে এটি গ্রাহকের জন্য এখনও এক-লাইনার, তবে এটি এটির পুরো বরাদ্দ জঞ্জালকে সরিয়ে দেয়। এছাড়াও, আপনি অ্যারের উপর ভিত্তি করে প্রাক-বরাদ্দ করতে পারেন।
মার্ক এ। ডোনোহো

4
আমি মনে করি না পারফরম্যান্স একটি সমস্যা হবে। সুইফ্ট সংকলকটি স্বীকৃতি দেবে যে অভিধানের পুরানো কপিগুলির কোনওটিই ব্যবহার করা হয় না এবং সম্ভবত সেগুলি তৈরিও করে না। আপনার নিজের কোডটি অপ্টিমাইজ করার প্রথম নিয়ম মনে রাখবেন: "এটি করবেন না" " কম্পিউটার বিজ্ঞানের আর একটি সর্বোচ্চতম ক্ষেত্রটি হ'ল দক্ষ অ্যালগরিদম কেবল তখনই কার্যকর হয় যখন এন বড় হয় এবং এন প্রায় কখনও বড় হয় না।
বগলুফ

136

যেহেতু Swift 4আপনি @ Tj3n এর পদ্ধতিকে আরও পরিষ্কার এবং দক্ষতার intoসাথে reduceএটির সংস্করণটি ব্যবহার করতে পারেন এটি অস্থায়ী অভিধান এবং রিটার্নের মান থেকে মুক্তি পাবে যাতে এটি দ্রুত পড়া এবং সহজ হয় is

নমুনা কোড সেটআপ:

struct Person { 
    let name: String
    let position: Int
}
let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]

Into ফলাফলের খালি অভিধানটি প্যারামিটারটি দিয়ে গেছে:

let myDict = myArray.reduce(into: [Int: String]()) {
    $0[$1.position] = $1.name
}

সরাসরি যেভাবে পাস করেছে তার একটি অভিধান সরাসরি দেয় into:

print(myDict) // [2: "c", 0: "h", 4: "b"]

আমি কেন কেবল অবস্থানগত ($ 0, $ 1) যুক্তি দিয়ে কাজ করি বলে মনে হয় তা নিয়ে আমি কিছুটা বিভ্রান্ত হয়ে পড়েছি এবং যখন আমি নাম রাখি না। সম্পাদনা: কিছুই নয়, সংকলক সম্ভবত বিচ্ছিন্ন হয়ে গেছে কারণ আমি এখনও ফলাফলটি ফেরত দেওয়ার চেষ্টা করেছি।
Departamento বি

এই উত্তরের মধ্যে অস্পষ্ট যে বিষয়টি $0প্রতিনিধিত্ব করে: এটি inoutঅভিধান যা আমরা "প্রত্যাবর্তন" করছি - যদিও সত্যিকার অর্থে প্রত্যাবর্তন করা হচ্ছে না, কারণ এটি পরিবর্তনের সাথে সাথে-ফিরে আসার সমতুল্য inout
ভবিষ্যতে-আদম 13

94

সুইফট 4 যেহেতু আপনি খুব সহজেই এটি করতে পারেন। আছে দুই নতুন initializers যে tuples একটি ক্রম থেকে (কী এবং মান যুক্ত জোড়া) একটি অভিধান নির্মাণ। কীগুলি অনন্য হওয়ার গ্যারান্টিযুক্ত থাকলে আপনি নিম্নলিখিতটি করতে পারেন:

let persons = [Person(name: "Franz", position: 1),
               Person(name: "Heinz", position: 2),
               Person(name: "Hans", position: 3)]

Dictionary(uniqueKeysWithValues: persons.map { ($0.position, $0.name) })

=> [1: "Franz", 2: "Heinz", 3: "Hans"]

কোনও কী ডুপ্লিকেট করা থাকলে এটি রানটাইম ত্রুটির সাথে ব্যর্থ হবে। সেক্ষেত্রে আপনি এই সংস্করণটি ব্যবহার করতে পারেন:

let persons = [Person(name: "Franz", position: 1),
               Person(name: "Heinz", position: 2),
               Person(name: "Hans", position: 1)]

Dictionary(persons.map { ($0.position, $0.name) }) { _, last in last }

=> [1: "Hans", 2: "Heinz"]

এটি আপনার লুপের মতো আচরণ করে। আপনি যদি মানগুলি "ওভাররাইট" করতে না চান এবং প্রথম ম্যাপিংটিতে আটকে থাকেন তবে আপনি এটি ব্যবহার করতে পারেন:

Dictionary(persons.map { ($0.position, $0.name) }) { first, _ in first }

=> [1: "Franz", 2: "Heinz"]

সুইফট 4.2 একটি যোগ তৃতীয় সূচনাকারী একটি অভিধানের মধ্যে যে গ্রুপ ক্রম উপাদান। অভিধান কীগুলি ক্লোজার দ্বারা উত্পন্ন হয়। একই কী সহ উপাদানগুলি ক্রম হিসাবে একই ক্রমে একটি অ্যারেতে রাখা হয়। এটি আপনাকে উপরের মতো একই ফলাফল অর্জন করতে দেয়। উদাহরণ স্বরূপ:

Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.last! }

=> [1: Person(name: "Hans", position: 1), 2: Person(name: "Heinz", position: 2)]

Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.first! }

=> [1: Person(name: "Franz", position: 1), 2: Person(name: "Heinz", position: 2)]


4
প্রকৃতপক্ষে ডক্স অনুসারে 'গ্রুপিং' ইনিশিয়ালাইজার অ্যারের সাথে অভিধানকে মান হিসাবে তৈরি করে (এটি 'গ্রুপিং' এর অর্থ, ঠিক?), এবং উপরের ক্ষেত্রে এটি আরও পছন্দসই হবে [1: [Person(name: "Franz", position: 1)], 2: [Person(name: "Heinz", position: 2)], 3: [Person(name: "Hans", position: 3)]]। প্রত্যাশিত ফলাফল নয়, তবে আপনি চাইলে সমতল অভিধানে আরও ম্যাপ করা যেতে পারে।
ভ্যারি

ইউরেকা! এই আমি খুঁজছি droid! এটি নিখুঁত এবং আমার কোডের জিনিসগুলি সহজতর করে l
জোনকার.ইন.জেনেভা

প্রারম্ভিকগুলির সাথে ডেড লিঙ্কগুলি। পারমিলিং ব্যবহার করতে দয়া করে আপডেট করতে পারেন?
মার্ক এ। ডোনহো

@ মার্কিআইভিভ আমি ভাঙ্গা লিঙ্কগুলি ঠিক করেছি। নিশ্চিত হতে পারছি না তোমার সাথে কি উল্লেখ করা হয় পার্মালিঙ্ক এই প্রেক্ষাপটে?
ম্যাকি মেসার

পারমালিঙ্কস হ'ল লিঙ্ক ওয়েবসাইটগুলি ব্যবহার করে যা কখনই পরিবর্তন হবে না। সুতরাং উদাহরণস্বরূপ, 'www.SomeSite.com/events/today/item1.htm' এর মতো কিছু যা পরিবর্তিত হতে পারে পরিবর্তে, বেশিরভাগ সাইটগুলি 'www.SomeSite.com?eventid=' এর মতো একটি দ্বিতীয় 'পারমালিঙ্ক' সেট করবে most 12345 'যা কখনই পরিবর্তন হবে না এবং সুতরাং লিঙ্কগুলিতে এটি নিরাপদ। প্রত্যেকে এটি করে না, তবে বড় সাইটগুলির বেশিরভাগ পৃষ্ঠাগুলি একটি অফার করবে।
মার্ক এ। ডোনোহো

8

আপনি Dictionaryটাইপের জন্য কাস্টম ইনিশিয়ালাইজার লিখতে পারেন , উদাহরণস্বরূপ টিপলস থেকে:

extension Dictionary {
    public init(keyValuePairs: [(Key, Value)]) {
        self.init()
        for pair in keyValuePairs {
            self[pair.0] = pair.1
        }
    }
}

এবং তারপরে mapআপনার অ্যারের জন্য ব্যবহার করুন Person:

var myDictionary = Dictionary(keyValuePairs: myArray.map{($0.position, $0.name)})

5

কীপথ ভিত্তিক সমাধান সম্পর্কে কীভাবে?

extension Array {

    func dictionary<Key, Value>(withKey key: KeyPath<Element, Key>, value: KeyPath<Element, Value>) -> [Key: Value] {
        return reduce(into: [:]) { dictionary, element in
            let key = element[keyPath: key]
            let value = element[keyPath: value]
            dictionary[key] = value
        }
    }
}

আপনি এটি এভাবে ব্যবহার করবেন:

struct HTTPHeader {

    let field: String, value: String
}

let headers = [
    HTTPHeader(field: "Accept", value: "application/json"),
    HTTPHeader(field: "User-Agent", value: "Safari"),
]

let allHTTPHeaderFields = headers.dictionary(withKey: \.field, value: \.value)

// allHTTPHeaderFields == ["Accept": "application/json", "User-Agent": "Safari"]

2

আপনি একটি হ্রাস ফাংশন ব্যবহার করতে পারেন। প্রথমে আমি ব্যক্তি শ্রেণির জন্য একটি মনোনীত আরম্ভকারী তৈরি করেছি

class Person {
  var name:String
  var position:Int

  init(_ n: String,_ p: Int) {
    name = n
    position = p
  }
}

পরে, আমি মানগুলির একটি অ্যারে শুরু করেছি

let myArray = [Person("Bill",1), 
               Person("Steve", 2), 
               Person("Woz", 3)]

এবং পরিশেষে, অভিধান ভেরিয়েবলের ফলাফল রয়েছে:

let dictionary = myArray.reduce([Int: Person]()){
  (total, person) in
  var totalMutable = total
  totalMutable.updateValue(person, forKey: total.count)
  return totalMutable
}

1

এটি আমি ব্যবহার করে যাচ্ছি

struct Person {
    let name:String
    let position:Int
}
let persons = [Person(name: "Franz", position: 1),
               Person(name: "Heinz", position: 2),
               Person(name: "Hans", position: 3)]

var peopleByPosition = [Int: Person]()
persons.forEach{peopleByPosition[$0.position] = $0}

যদি সেখানে যাতে গত 2 লাইন একত্রিত করার জন্য একটি উপায় ছিল চমৎকার হবে peopleByPositionএকটি হতে পারে let

আমরা অ্যারেতে এটি বাড়িয়ে দিতে পারি!

extension Array {
    func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable {
        var map = [T: Element]()
        self.forEach{ map[block($0)] = $0 }
        return map
    }
}

তারপরে আমরা ঠিক করতে পারি

let peopleByPosition = persons.mapToDict(by: {$0.position})

1
extension Array {
    func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable {
        var map = [T: Element]()
        self.forEach{ map[block($0)] = $0 }
        return map
    }
}

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