swift - সুইফট-একাধিক মানদণ্ডের সাথে অবজেক্টগুলির অ্যারে বাছাই করুন




sorting (6)

আমার পরিচিতিগুলির একটি অ্যারে রয়েছে:

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

যোগাযোগ শ্রেণি:

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

এবং আমি অ্যারের lastName বাছাই করতে চাই এবং তারপরে firstName ক্ষেত্রে যদি কিছু পরিচিতি একই lastName

আমি এই মানদণ্ডগুলির মধ্যে একটি অনুসারে বাছাই করতে সক্ষম, তবে উভয়ই নয়।

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

এই অ্যারেটি বাছাই করতে আমি কীভাবে আরও মানদণ্ড যুক্ত করতে পারি?


একাধিক মানদণ্ডের তুলনা করতে টিপলস ব্যবহার করা

একাধিক মাপদণ্ড অনুসারে বাছাই করার সত্যিই সহজ উপায় (যেমন একটি তুলনা অনুসারে বাছাই করা, এবং যদি সমতুল্য হয়, তবে অন্য তুলনা দ্বারা) হল টিপলস ব্যবহার করে, কারণ < এবং > অপারেটরদের কাছে লিক্সিকোগ্রাফিক তুলনা সম্পাদনকারীদের জন্য ওভারলোড রয়েছে।

/// 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 পরবর্তী জোড় উপাদানের দিকে চলে যাবে, অর্থাত্ 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 আরোহণ lastName বাছাই করবে।

একটি sort(by:) সংজ্ঞা দেওয়া sort(by:) ওভারলোড যা একাধিক পূর্বাভাস নেয় takes

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:) 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 পরামর্শ অনুসারে আপনি @AMomchilov তুলনা করার Contact সামঞ্জস্য করতে পারেন:

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() কল করুন 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)

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

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.<(_:_:) আপনার প্রয়োগের জন্য পরামর্শ করে Comparable.<(_:_:) উপাদানগুলি কীভাবে তুলনা করে তা নির্ধারণ করতে অপারেটর । এইভাবে, আপনি Contact কোনও Sequence বাছাইকরণের কোডটি কখনও নকল না করেই বাছাই করতে পারেন।


@ হামিশের বর্ণিত হিসাবে ডিক্সিকোগ্রাফিকগুলি যে জিনিসটি করতে পারে না তা হ'ল বিভিন্ন সাজানোর দিকনির্দেশনা পরিচালনা করা, প্রথম ক্ষেত্রের উতরাই অনুসারে বাছাই করুন, পরবর্তী ক্ষেত্রের আরোহণ ইত্যাদি etc.

আমি কীভাবে এটি সুইফট 3 এ ব্লগ পোস্ট তৈরি করেছি এবং কোডটি সহজ এবং পঠনযোগ্য রাখি।

আপনি এখানে পেতে পারেন:

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

আপনি এখানে কোড সহ একটি গিটহাবের সংগ্রহস্থল খুঁজে পেতে পারেন:

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
        )

আমি হামিশের টিপল সলিউশনটি ব্যবহার করার পরামর্শ দিচ্ছি কারণ এটি অতিরিক্ত কোডের প্রয়োজন হয় না।

আপনি যদি এমন কিছু চান যা স্টেটমেন্টগুলির মতো আচরণ করে 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 পরিবর্তে $0.family পরিবর্তে। এবং আরোহী / উতরাই ওভারলোডেড অপারেটরের পরিবর্তে প্যারামিটার দ্বারা নিয়ন্ত্রিত হয়। জেমির সমাধান অ্যারেতে একটি এক্সটেনশন যুক্ত করে যেখানে আমার সমাধানটি sorted / sorted পদ্ধতিতে অন্তর্নির্মিত ব্যবহার করে তবে আরও দুটি অতিরিক্ত সংজ্ঞা দেওয়া দরকার: compare এবং comparisons

সম্পূর্ণতার জন্য, আমার সমাধানটি কীভাবে হামিশের টিপল সমাধানের সাথে তুলনা করে। প্রদর্শনের জন্য আমি একটি বুনো উদাহরণ ব্যবহার করব যেখানে আমরা লোকদের (name, address, profileViews) বাছাই করতে চাই হামিশের সমাধানটি তুলনা শুরুর আগে একবার 6 টি সম্পত্তি মানকে মূল্যায়ন করবে। এটি পছন্দসই বা নাও থাকতে পারে। উদাহরণস্বরূপ, ধরে নেওয়া প্রোফাইলভিউগুলি একটি ব্যয়বহুল নেটওয়ার্ক কল যা আমরা একেবারেই প্রয়োজনীয় না হলে প্রোফাইল profileViews কল এড়াতে চাই profileViews আমার সমাধান প্রোফাইল $0.name == $1.name এবং $0.address == $1.address $0.name == $1.name পর্যন্ত মূল্যায়ন এড়াতে $0.address == $1.address । যাইহোক, যখন এটি প্রোফাইলভিউগুলি মূল্যায়ন করে তখন সম্ভবত একবারের চেয়ে আরও অনেকবার মূল্যায়ন করবে।


কেমন:

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

যা সুইফট 3 এ আমার অ্যারে [স্ট্রিং] এর জন্য কাজ করেছিল এবং এটি সুইফট 4-তে মনে হয় ঠিক আছে

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




sorting