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}