स्विफ्ट - कई मानदंडों के साथ वस्तुओं की क्रमबद्ध सरणी


92

मेरे पास Contactवस्तुओं की एक सरणी है :

var contacts:[Contact] = [Contact]()

संपर्क वर्ग:

Class Contact:NSOBject {
    var firstName:String!
    var lastName:String!
}

और मैं उस सरणी को क्रमबद्ध करना चाहूंगा lastNameऔर तब तक firstNameकुछ संपर्क समान हो जाएंगे lastName

मैं उन मानदंडों में से एक के आधार पर छांटने में सक्षम हूं, लेकिन दोनों में नहीं।

contacts.sortInPlace({$0.lastName < $1.lastName})

मैं इस सरणी को सॉर्ट करने के लिए और अधिक मापदंड कैसे जोड़ सकता हूं?


2
जैसा आपने अभी कहा वैसा ही करें! घुंघराले ब्रेसिज़ के अंदर आपका कोड कहना चाहिए: "यदि अंतिम नाम समान हैं, तो पहले नाम से छाँटें; अन्यथा अंतिम नाम से छाँटें"।
मैट

4
मुझे यहाँ कुछ कोड की महक आ रही है: 1) Contactशायद NSObject2 से विरासत में नहीं आना चाहिए , 2) Contactशायद एक संरचना होनी चाहिए, और 3) firstNameऔर lastNameसंभवतः अंतर्निहित अलिखित वैकल्पिक नहीं होना चाहिए।
अलेक्जेंडर - मोनिका

3
@AMomchilov से संपर्क करने का सुझाव देने का कोई कारण नहीं है क्योंकि आपको पता होना चाहिए कि क्या आप नहीं जानते कि उसका बाकी कोड पहले से ही इसके उदाहरणों का उपयोग करने में संदर्भ शब्दार्थों पर निर्भर है।
पैट्रिक गोले

3
@AMomchilov "संभवतः" भ्रामक है क्योंकि आप बाकी कोडबेस के बारे में कुछ भी नहीं जानते हैं। यदि इसे एक संरचना में बदल दिया जाता है, तो हाथ पर मौजूद उदाहरण को संशोधित करने के बजाय, अचानक परिवर्तन करते हुए सभी प्रतियां उत्पन्न की जाती हैं। यह व्यवहार में एक व्यापक बदलाव है और ऐसा करने से बग में "शायद" परिणाम होगा क्योंकि यह सब कुछ संदर्भ और मूल्य शब्दार्थ दोनों के लिए ठीक से कोड नहीं किया गया है ।
पैट्रिक गोले

1
@AMomchilov ने अभी तक एक कारण नहीं सुना है कि यह संभवतः एक संरचना क्यों होनी चाहिए। मुझे नहीं लगता कि ओपी उन सुझावों की सराहना करेगा जो उनके कार्यक्रम के बाकी हिस्सों के शब्दार्थों को संशोधित करते हैं, खासकर जब यह समस्या को हल करने के लिए आवश्यक नहीं था। एहसास नहीं था कि संकलक नियम कुछ के लिए कानूनी थे ... शायद मैं गलत वेबसाइट पर हूँ
पैट्रिक गोले

जवाबों:


120

सोचें कि "कई मानदंडों द्वारा छँटाई" का अर्थ क्या है। इसका मतलब है कि दो वस्तुओं की तुलना पहले एक मानदंड से की जाती है। फिर, यदि वे मानदंड समान हैं, तो अगले मानदंड से संबंध टूट जाएंगे, और तब तक जब तक आपको वांछित ऑर्डर नहीं मिलता।

let sortedContacts = contacts.sort {
    if $0.lastName != $1.lastName { // first, compare by last names
        return $0.lastName < $1.lastName
    }
    /*  last names are the same, break ties by foo
    else if $0.foo != $1.foo {
        return $0.foo < $1.foo
    }
    ... repeat for all other fields in the sorting
    */
    else { // All other fields are tied, break ties by last name
        return $0.firstName < $1.firstName
    }
}

आप यहां जो देख रहे हैं वह Sequence.sorted(by:)तरीका है , जो प्रदान करती है कि तत्वों की तुलना कैसे की जाती है।

यदि आपकी छंटाई का उपयोग कई स्थानों पर किया जाएगा, तो अपने प्रकार को Comparable प्रोटोकॉल के अनुरूप बनाना बेहतर हो सकता है । इस तरह, आप Sequence.sorted()विधि का उपयोग कर सकते हैं , जो आपके तत्वों को तुलना करने के तरीके को निर्धारित करने के लिए Comparable.<(_:_:)ऑपरेटर के आपके कार्यान्वयन को सलाह देता है । इस तरह, आप किसी भी सॉर्ट कर सकते हैं Sequenceकी Contactकभी छँटाई कोड नकल के बिना है।


2
elseशरीर के बीच होना चाहिए { ... }अन्यथा कोड संकलन नहीं है।
लुका एंजेलेटी

समझ गया। मैं इसे लागू करने की कोशिश की, लेकिन वाक्यविन्यास अधिकार नहीं मिल सका। बहुत बहुत धन्यवाद।
sbkl

के लिए sortबनाम sortInPlaceदेखने के लिए यहाँ । Aslo इसे नीचे देखें, यह बहुत अधिक मॉड्यूलर है
Honey

sortInPlaceअब स्विफ्ट 3 में उपलब्ध नहीं है, इसके बजाय आपको इसका उपयोग करना होगा sort()sort()सरणी को ही म्यूट करेगा। इसके अलावा वहाँ एक नया नाम दिया है समारोह है sorted()जो एक वापस आ जाएगी क्रमबद्ध सरणी
हनी

2
@AthanasiusOfAlex का उपयोग करना ==एक अच्छा विचार नहीं है। यह केवल 2 गुणों के लिए काम करता है। इससे अधिक कोई भी, और आप बहुत सारे मिश्रित बूलियन भावों के साथ खुद को दोहराना शुरू करते हैं
अलेक्जेंडर - बहाल मोनिका

122

कई मानदंडों की तुलना करने के लिए टुपल्स का उपयोग करना

कई मानदंडों द्वारा एक प्रकार का प्रदर्शन करने का वास्तव में सरल तरीका है (अर्थात एक तुलना द्वारा क्रमबद्ध करना, और यदि समकक्ष, तो दूसरी तुलना द्वारा) का उपयोग ट्यूपल्स द्वारा किया जाता है , क्योंकि ऑपरेटर <और >उनके लिए अधिभार हैं जो लेक्सिकोग्राफ़िक तुलना करते हैं।

/// Returns a Boolean value indicating whether the first tuple is ordered
/// before the second in a lexicographical ordering.
///
/// Given two tuples `(a1, a2, ..., aN)` and `(b1, b2, ..., bN)`, the first
/// tuple is before the second tuple if and only if
/// `a1 < b1` or (`a1 == b1` and
/// `(a2, ..., aN) < (b2, ..., bN)`).
public func < <A : Comparable, B : Comparable>(lhs: (A, B), rhs: (A, B)) -> Bool

उदाहरण के लिए:

struct Contact {
  var firstName: String
  var lastName: String
}

var contacts = [
  Contact(firstName: "Leonard", lastName: "Charleson"),
  Contact(firstName: "Michael", lastName: "Webb"),
  Contact(firstName: "Charles", lastName: "Alexson"),
  Contact(firstName: "Michael", lastName: "Elexson"),
  Contact(firstName: "Alex", lastName: "Elexson"),
]

contacts.sort {
  ($0.lastName, $0.firstName) <
    ($1.lastName, $1.firstName)
}

print(contacts)

// [
//   Contact(firstName: "Charles", lastName: "Alexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Webb")
// ]

यह lastNameपहले तत्वों के गुणों की तुलना करेगा । यदि वे समान नहीं हैं, तो <उनके साथ तुलना के आधार पर क्रम क्रम आधारित होगा । वे तो हैं बराबर है, तो यह टपल में तत्वों की अगले जोड़ी पर ले जाते हैं, की तुलना यानी जाएगा firstNameगुण।

मानक पुस्तकालय 2 से 6 तत्वों के टुपल्स के लिए प्रदान करता है <और >ओवरलोड करता है ।

यदि आप अलग-अलग गुणों के लिए अलग-अलग सॉर्टिंग ऑर्डर चाहते हैं, तो आप टुपल्स में तत्वों को स्वैप कर सकते हैं:

contacts.sort {
  ($1.lastName, $0.firstName) <
    ($0.lastName, $1.firstName)
}

// [
//   Contact(firstName: "Michael", lastName: "Webb")
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Charles", lastName: "Alexson"),
// ]

यह अब lastNameअवरोही, फिर firstNameआरोही द्वारा क्रमबद्ध होगा ।


एक sort(by:)अधिभार को परिभाषित करना जो कई विधेय लेता है

क्लोजर और SortDescriptors के साथ सॉर्टिंग कलेक्शंसmap पर चर्चा से प्रेरित , एक अन्य विकल्प एक कस्टम अधिभार को परिभाषित करना होगा sort(by:)और sorted(by:)जो कई विधेयकों से संबंधित होता है - जहां तत्वों के क्रम को तय करने के लिए प्रत्येक विधेय को बारी-बारी से माना जाता है।

extension MutableCollection where Self : RandomAccessCollection {
  mutating func sort(
    by firstPredicate: (Element, Element) -> Bool,
    _ secondPredicate: (Element, Element) -> Bool,
    _ otherPredicates: ((Element, Element) -> Bool)...
  ) {
    sort(by:) { lhs, rhs in
      if firstPredicate(lhs, rhs) { return true }
      if firstPredicate(rhs, lhs) { return false }
      if secondPredicate(lhs, rhs) { return true }
      if secondPredicate(rhs, lhs) { return false }
      for predicate in otherPredicates {
        if predicate(lhs, rhs) { return true }
        if predicate(rhs, lhs) { return false }
      }
      return false
    }
  }
}

extension Sequence {
  mutating func sorted(
    by firstPredicate: (Element, Element) -> Bool,
    _ secondPredicate: (Element, Element) -> Bool,
    _ otherPredicates: ((Element, Element) -> Bool)...
  ) -> [Element] {
    return sorted(by:) { lhs, rhs in
      if firstPredicate(lhs, rhs) { return true }
      if firstPredicate(rhs, lhs) { return false }
      if secondPredicate(lhs, rhs) { return true }
      if secondPredicate(rhs, lhs) { return false }
      for predicate in otherPredicates {
        if predicate(lhs, rhs) { return true }
        if predicate(rhs, lhs) { return false }
      }
      return false
    }
  }
}

( secondPredicate:पैरामीटर दुर्भाग्यपूर्ण है, लेकिन मौजूदा sort(by:)अधिभार के साथ अस्पष्टता पैदा करने से बचने के लिए आवश्यक है )

यह तब हमें कहने की अनुमति देता है ( contactsपहले से सरणी का उपयोग करके ):

contacts.sort(by:
  { $0.lastName > $1.lastName },  // first sort by lastName descending
  { $0.firstName < $1.firstName } // ... then firstName ascending
  // ...
)

print(contacts)

// [
//   Contact(firstName: "Michael", lastName: "Webb")
//   Contact(firstName: "Alex", lastName: "Elexson"),
//   Contact(firstName: "Michael", lastName: "Elexson"),
//   Contact(firstName: "Leonard", lastName: "Charleson"),
//   Contact(firstName: "Charles", lastName: "Alexson"),
// ]

// or with sorted(by:)...
let sortedContacts = contacts.sorted(by:
  { $0.lastName > $1.lastName },  // first sort by lastName descending
  { $0.firstName < $1.firstName } // ... then firstName ascending
  // ...
)

यद्यपि कॉल-साइट टपल संस्करण के रूप में संक्षिप्त नहीं है, आप इसकी तुलना किस क्रम और किस क्रम में कर रहे हैं, के साथ अतिरिक्त स्पष्टता प्राप्त करते हैं।


के अनुरूप करना Comparable

यदि आप इस प्रकार की तुलना नियमित रूप से करने जा रहे हैं, तो @AMomchilov & @appzYourLife के अनुसार , आप इसके अनुरूप Contactहो सकते हैं Comparable:

extension Contact : Comparable {
  static func == (lhs: Contact, rhs: Contact) -> Bool {
    return (lhs.firstName, lhs.lastName) ==
             (rhs.firstName, rhs.lastName)
  }

  static func < (lhs: Contact, rhs: Contact) -> Bool {
    return (lhs.lastName, lhs.firstName) <
             (rhs.lastName, rhs.firstName)
  }
}

और अब बस sort()आरोही क्रम के लिए कॉल करें :

contacts.sort()

या sort(by: >)एक अवरोही क्रम के लिए:

contacts.sort(by: >)

नेस्टेड प्रकार में कस्टम प्रकार के आदेशों को परिभाषित करना

यदि आपके पास अन्य प्रकार के आदेश हैं जिनका आप उपयोग करना चाहते हैं, तो आप उन्हें एक नेस्टेड प्रकार में परिभाषित कर सकते हैं:

extension Contact {
  enum Comparison {
    static let firstLastAscending: (Contact, Contact) -> Bool = {
      return ($0.firstName, $0.lastName) <
               ($1.firstName, $1.lastName)
    }
  }
}

और फिर बस के रूप में कॉल करें:

contacts.sort(by: Contact.Comparison.firstLastAscending)

contacts.sort { ($0.lastName, $0.firstName) < ($1.lastName, $1.firstName) } मदद की। धन्यवाद
प्रभाकर कासी

अगर मेरी तरह, छांटे जाने वाले गुण वैकल्पिक हैं, तो आप कुछ इस तरह से कर सकते हैं contacts.sort { ($0.lastName ?? "", $0.firstName ?? "") < ($1.lastName ?? "", $1.firstName ?? "") }:।
बॉबकॉव

वेश्या! इतना सरल अभी तक इतना कुशल ... क्यों मैं उस के बारे में कभी नहीं सुना है ?! आपका बहुत बहुत धन्यवाद!
एथेनिल

@ याकूबकैवे आपको इस बात की दया पर छोड़ देता है कि ""अन्य तारों की तुलना कैसे की जाती है (यह गैर-खाली तारों से पहले आता है)। यदि आप nilइसके बजाय सूची के अंत में आना चाहते हैं तो यह अंतर्निहित, थोड़े जादू और अनम्य है । मेरा सुझाव है कि आप मेरे nilComparatorकार्य स्टैकओवरफ़्लो
।.com/a/44808567/3141234

19

2 मानदंडों के साथ छंटनी के लिए एक और सरल दृष्टिकोण नीचे दिखाया गया है।

पहले क्षेत्र की जाँच करें, इस मामले में यह है lastName, अगर वे समान क्रमांक नहीं हैं lastName, यदि lastNameसमान हैं, तो इस मामले में, दूसरे क्षेत्र के आधार पर छाँटें firstName

contacts.sort { $0.lastName == $1.lastName ? $0.firstName < $1.firstName : $0.lastName < $1.lastName  }

यह ट्यूपल्स की तुलना में अधिक लचीलापन देता है।
बेबाक

5

एक चीज़ जो लेक्सोग्राफिकल प्रकार से नहीं हो सकती है जैसा कि @ Hamish द्वारा वर्णित है, अलग-अलग सॉर्टिंग दिशाओं को संभालना है, पहले क्षेत्र के अवरोही द्वारा सॉर्ट करना, अगले फ़ील्ड का आरोही होना, आदि।

मैंने स्विफ्ट 3 में इस पर एक ब्लॉग पोस्ट बनाई और कोड को सरल और पठनीय रखा।

आप इसे यहां देख सकते हैं:

http://master-method.com/index.php/2016/11/23/sort-a-sequence-ie-arrays-of-objects-by-multiple-properties-in-swift-3/

आप यहां कोड के साथ GitHub रिपॉजिटरी भी पा सकते हैं:

https://github.com/jallauca/SortByMultipleFieldsSwift.playground

इसके बारे में सभी का कहना है, यदि आपके पास स्थानों की सूची है, तो आप ऐसा कर पाएंगे:

struct Location {
    var city: String
    var county: String
    var state: String
}

var locations: [Location] {
    return [
        Location(city: "Dania Beach", county: "Broward", state: "Florida"),
        Location(city: "Fort Lauderdale", county: "Broward", state: "Florida"),
        Location(city: "Hallandale Beach", county: "Broward", state: "Florida"),
        Location(city: "Delray Beach", county: "Palm Beach", state: "Florida"),
        Location(city: "West Palm Beach", county: "Palm Beach", state: "Florida"),
        Location(city: "Savannah", county: "Chatham", state: "Georgia"),
        Location(city: "Richmond Hill", county: "Bryan", state: "Georgia"),
        Location(city: "St. Marys", county: "Camden", state: "Georgia"),
        Location(city: "Kingsland", county: "Camden", state: "Georgia"),
    ]
}

let sortedLocations =
    locations
        .sorted(by:
            ComparisonResult.flip <<< Location.stateCompare,
            Location.countyCompare,
            Location.cityCompare
        )

1
"एक चीज जो लेक्सोग्राफिक प्रकार से नहीं हो सकती है जैसा कि
हैमिश

मुझे यह एक दिलचस्प सैद्धांतिक अभ्यास लगता है लेकिन @ हैमिश के उत्तर की तुलना में अधिक जटिल है। मेरी राय में कम कोड बेहतर कोड है।
मैनुअल

5

इस सवाल के पहले से ही कई शानदार जवाब हैं, लेकिन मैं एक लेख की ओर इशारा करना चाहता हूं - स्विफ्ट में सॉर्ट डेस्क्रिप्टर्स । हमारे पास कई मानदंड छांटने के कई तरीके हैं।

  1. NSSortDescriptor का उपयोग करते हुए, इस तरह से कुछ सीमाएं हैं, ऑब्जेक्ट को एक वर्ग होना चाहिए और NSObject से विरासत में मिला।

    class Person: NSObject {
        var first: String
        var last: String
        var yearOfBirth: Int
        init(first: String, last: String, yearOfBirth: Int) {
            self.first = first
            self.last = last
            self.yearOfBirth = yearOfBirth
        }
    
        override var description: String {
            get {
                return "\(self.last) \(self.first) (\(self.yearOfBirth))"
            }
        }
    }
    
    let people = [
        Person(first: "Jo", last: "Smith", yearOfBirth: 1970),
        Person(first: "Joe", last: "Smith", yearOfBirth: 1970),
        Person(first: "Joe", last: "Smyth", yearOfBirth: 1970),
        Person(first: "Joanne", last: "smith", yearOfBirth: 1985),
        Person(first: "Joanne", last: "smith", yearOfBirth: 1970),
        Person(first: "Robert", last: "Jones", yearOfBirth: 1970),
    ]
    

    यहाँ, उदाहरण के लिए, हम अंतिम नाम के आधार पर छाँटना चाहते हैं, फिर पहला नाम, अंत में जन्म वर्ष। और हम चाहते हैं कि यह असंवेदनशील हो और उपयोगकर्ता के लोकेल का उपयोग कर रहा हो।

    let lastDescriptor = NSSortDescriptor(key: "last", ascending: true,
      selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
    let firstDescriptor = NSSortDescriptor(key: "first", ascending: true, 
      selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
    let yearDescriptor = NSSortDescriptor(key: "yearOfBirth", ascending: true)
    
    
    
    (people as NSArray).sortedArray(using: [lastDescriptor, firstDescriptor, yearDescriptor]) 
    // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]
    
  2. अंतिम नाम / पहले नाम के साथ छँटाई के स्विफ्ट तरीके का उपयोग करना। इस तरह से क्लास / स्ट्रक्चर दोनों के साथ काम करना चाहिए। हालाँकि, हम यहाँ yearOfBirth द्वारा सॉर्ट नहीं करते हैं।

    let sortedPeople = people.sorted { p0, p1 in
        let left =  [p0.last, p0.first]
        let right = [p1.last, p1.first]
    
        return left.lexicographicallyPrecedes(right) {
            $0.localizedCaseInsensitiveCompare($1) == .orderedAscending
        }
    }
    sortedPeople // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1985), Joanne smith (1970), Joe Smith (1970), Joe Smyth (1970)]
    
  3. NSSortDescriptor को चालू करने के लिए स्विफ्ट तरीका। यह इस अवधारणा का उपयोग करता है कि 'कार्य एक प्रथम श्रेणी के प्रकार हैं'। SortDescriptor एक फ़ंक्शन प्रकार है, दो मान लेता है, एक बूल लौटाता है। SortByFirstName कहें कि हम दो पैरामीटर ($ 0, $ 1) लेते हैं और उनके पहले नामों की तुलना करते हैं। संयोजन कार्य SortDescriptors का एक गुच्छा लेता है, उनमें से सभी की तुलना करें और ऑर्डर दें।

    typealias SortDescriptor<Value> = (Value, Value) -> Bool
    
    let sortByFirstName: SortDescriptor<Person> = {
        $0.first.localizedCaseInsensitiveCompare($1.first) == .orderedAscending
    }
    let sortByYear: SortDescriptor<Person> = { $0.yearOfBirth < $1.yearOfBirth }
    let sortByLastName: SortDescriptor<Person> = {
        $0.last.localizedCaseInsensitiveCompare($1.last) == .orderedAscending
    }
    
    func combine<Value>
        (sortDescriptors: [SortDescriptor<Value>]) -> SortDescriptor<Value> {
        return { lhs, rhs in
            for isOrderedBefore in sortDescriptors {
                if isOrderedBefore(lhs,rhs) { return true }
                if isOrderedBefore(rhs,lhs) { return false }
            }
            return false
        }
    }
    
    let combined: SortDescriptor<Person> = combine(
        sortDescriptors: [sortByLastName,sortByFirstName,sortByYear]
    )
    people.sorted(by: combined)
    // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]
    

    यह अच्छा है क्योंकि आप इसे संरचना और वर्ग दोनों के साथ उपयोग कर सकते हैं, आप इसे निल्स के साथ तुलना करने के लिए भी बढ़ा सकते हैं।

फिर भी, मूल लेख पढ़ने का जोरदार सुझाव दिया जाता है। इसमें बहुत अधिक विवरण और अच्छी तरह से समझाया गया है।


2

मैं हामिश के टुपल सॉल्यूशन का उपयोग करने की सलाह दूंगा क्योंकि इसके लिए अतिरिक्त कोड की आवश्यकता नहीं है।


यदि आप कुछ ऐसा चाहते हैं जो ifबयानों की तरह व्यवहार करता है, लेकिन शाखा तर्क को सरल करता है, तो आप इस समाधान का उपयोग कर सकते हैं, जो आपको निम्नलिखित करने की अनुमति देता है:

animals.sort {
  return comparisons(
    compare($0.family, $1.family, ascending: false),
    compare($0.name, $1.name))
}

यहां ऐसे कार्य हैं जो आपको ऐसा करने की अनुमति देते हैं:

func compare<C: Comparable>(_ value1Closure: @autoclosure @escaping () -> C, _ value2Closure: @autoclosure @escaping () -> C, ascending: Bool = true) -> () -> ComparisonResult {
  return {
    let value1 = value1Closure()
    let value2 = value2Closure()
    if value1 == value2 {
      return .orderedSame
    } else if ascending {
      return value1 < value2 ? .orderedAscending : .orderedDescending
    } else {
      return value1 > value2 ? .orderedAscending : .orderedDescending
    }
  }
}

func comparisons(_ comparisons: (() -> ComparisonResult)...) -> Bool {
  for comparison in comparisons {
    switch comparison() {
    case .orderedSame:
      continue // go on to the next property
    case .orderedAscending:
      return true
    case .orderedDescending:
      return false
    }
  }
  return false // all of them were equal
}

यदि आप इसका परीक्षण करना चाहते हैं, तो आप इस अतिरिक्त कोड का उपयोग कर सकते हैं:

enum Family: Int, Comparable {
  case bird
  case cat
  case dog

  var short: String {
    switch self {
    case .bird: return "B"
    case .cat: return "C"
    case .dog: return "D"
    }
  }

  public static func <(lhs: Family, rhs: Family) -> Bool {
    return lhs.rawValue < rhs.rawValue
  }
}

struct Animal: CustomDebugStringConvertible {
  let name: String
  let family: Family

  public var debugDescription: String {
    return "\(name) (\(family.short))"
  }
}

let animals = [
  Animal(name: "Leopard", family: .cat),
  Animal(name: "Wolf", family: .dog),
  Animal(name: "Tiger", family: .cat),
  Animal(name: "Eagle", family: .bird),
  Animal(name: "Cheetah", family: .cat),
  Animal(name: "Hawk", family: .bird),
  Animal(name: "Puma", family: .cat),
  Animal(name: "Dalmatian", family: .dog),
  Animal(name: "Lion", family: .cat),
]

जेमी के समाधान से मुख्य अंतर यह है कि गुणों की पहुंच को क्लास में स्थिर / आवृत्ति विधियों के बजाय इनलाइन परिभाषित किया गया है। के $0.familyबजाय एग Animal.familyCompare। और आरोही / उतरने को एक अधिभारित ऑपरेटर के बजाय एक पैरामीटर द्वारा नियंत्रित किया जाता है। जेमी का समाधान ऐरे पर एक एक्सटेंशन जोड़ता है जबकि मेरा समाधान अंतर्निहित sort/ sortedविधि का उपयोग करता है लेकिन दो अतिरिक्त लोगों को परिभाषित करने की आवश्यकता होती है: compareऔर comparisons

संपूर्णता के लिए, यहां बताया गया है कि हामिश के टुपल सॉल्यूशन की तुलना मेरा समाधान कैसे करता है । यह प्रदर्शित करने के लिए कि हम एक जंगली उदाहरण का उपयोग करेंगे जहां हम (name, address, profileViews)हैमिश के समाधान से लोगों को छाँटना चाहते हैं, तुलना शुरू होने से पहले एक बार 6 संपत्ति मूल्यों में से प्रत्येक का मूल्यांकन करेंगे। यह वांछित हो भी सकता है और नहीं भी। उदाहरण के लिए, मान profileViewsलेना एक महंगा नेटवर्क कॉल है जिसे हम कॉल करने से बचना चाहते हैं profileViewsजब तक कि यह बिल्कुल आवश्यक न हो। मेरे समाधान का मूल्यांकन करने से दूर रहेंगे profileViewsजब तक $0.name == $1.nameऔर $0.address == $1.address। हालाँकि, जब यह मूल्यांकन करता है तो यह profileViewsएक बार की तुलना में कई गुना अधिक मूल्यांकन करेगा।


1

कैसा रहेगा:

contacts.sort() { [$0.last, $0.first].lexicographicalCompare([$1.last, $1.first]) }

lexicographicallyPrecedesसरणी में सभी प्रकार के समान होने की आवश्यकता है। उदाहरण के लिए [String, String]। ओपी शायद चाहता है कि मिश्रण और प्रकारों को मिलाएं: [String, Int, Bool]इसलिए वे ऐसा कर सकते थे [$0.first, $0.age, $0.isActive]
सेंसफुल

-1

कि स्विफ्ट 3 में मेरे एरे [स्ट्रिंग] के लिए काम किया है और यह स्विफ्ट 4 में लगता है ठीक है

array = array.sorted{$0.compare($1, options: .numeric) == .orderedAscending}

क्या आपने उत्तर देने से पहले प्रश्न पढ़ा था? कई मापदंडों द्वारा क्रमबद्ध करें, एक नहीं, जो आप प्रस्तुत करते हैं।
Vive
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.