我有一个Contact对象数组:
var contacts:[Contact] = [Contact]()
Run Code Online (Sandbox Code Playgroud)
联系班级:
Class Contact:NSOBject {
var firstName:String!
var lastName:String!
}
Run Code Online (Sandbox Code Playgroud)
我希望按顺序对该数组进行排序,lastName然后firstName在某些联系人得到相同的情况下lastName.
我可以按照其中一个标准排序,但不能同时排序.
contacts.sortInPlace({$0.lastName < $1.lastName})
Run Code Online (Sandbox Code Playgroud)
如何添加更多条件来对此数组进行排序?
Ale*_*ica 91
想想"按多个标准排序"的含义.这意味着首先通过一个标准比较两个对象.然后,如果这些标准相同,则将通过下一个标准打破关系,依此类推,直到获得所需的排序.
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
}
}
Run Code Online (Sandbox Code Playgroud)
你在这里看到的是Sequence.sorted(by:)方法,它参考提供的闭包来确定元素的比较方式.
如果您的排序将在许多地方使用,那么最好使您的类型符合Comparable 协议.这样,您可以使用Sequence.sorted()方法,该方法参考您的Comparable.<(_:_:)运算符实现来确定元素的比较方式.这样,您可以对任何s进行排序Sequence,Contact而无需复制排序代码.
Ham*_*ish 83
由多个条件执行排序的一个非常简单的方式(由一个比较即排序,并且如果等效,然后通过另一比较)是通过使用元组,作为<和>运营商执行字典式比较他们的重载.
/// 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
Run Code Online (Sandbox Code Playgroud)
例如:
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")
// ]
Run Code Online (Sandbox Code Playgroud)
这将首先比较元素的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"),
// ]
Run Code Online (Sandbox Code Playgroud)
现在将按lastName降序排序,然后firstName按升序排序.
sort(by:)需要多个谓词的重载受关于使用map闭包和SortDescriptors 对集合进行排序的讨论的启发,另一种选择是定义自定义重载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
}
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
(该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
// ...
)
Run Code Online (Sandbox Code Playgroud)
虽然呼叫站点不像元组变体那样简洁,但您可以更清楚地了解所比较的内容和顺序.
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)
}
}
Run Code Online (Sandbox Code Playgroud)
现在只需要sort()升序:
contacts.sort()
Run Code Online (Sandbox Code Playgroud)
或者sort(by: >)降序排列:
contacts.sort(by: >)
Run Code Online (Sandbox Code Playgroud)
如果您要使用其他排序顺序,可以使用嵌套类型定义它们:
extension Contact {
enum Comparison {
static let firstLastAscending: (Contact, Contact) -> Bool = {
return ($0.firstName, $0.lastName) <
($1.firstName, $1.lastName)
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后简单地称为:
contacts.sort(by: Contact.Comparison.firstLastAscending)
Run Code Online (Sandbox Code Playgroud)
oya*_*lhi 11
使用2个标准进行分类的另一种简单方法如下所示.
检查第一个字段,在这种情况下lastName,如果它们不相等排序lastName,如果lastName相等,则按第二个字段排序,在这种情况下firstName.
contacts.sort { $0.lastName == $1.lastName ? $0.firstName < $1.firstName : $0.lastName < $1.lastName }
Run Code Online (Sandbox Code Playgroud)
小智 5
@Hamish描述的字典排序不能做的一件事就是处理不同的排序方向,比如按第一个字段降序排序,下一个字段升序排序等.
我在Swift 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
)
Run Code Online (Sandbox Code Playgroud)
这个问题已有很多很好的答案,但我想指出一篇文章 - Swift中的Sort Descriptors.我们有多种方法可以进行多重标准排序.
使用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),
]
Run Code Online (Sandbox Code Playgroud)
例如,在这里,我们希望按姓氏排序,然后按名字排序,最后按出生年份排序.我们希望不区分大小写并使用用户的语言环境.
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)]
Run Code Online (Sandbox Code Playgroud)使用Swift方式对姓氏/名字进行排序.这种方式应该适用于class/struct.但是,我们不会在这里按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)]
Run Code Online (Sandbox Code Playgroud)快速的方式来传播NSSortDescriptor.这使用了"函数是一流类型"的概念.SortDescriptor是一个函数类型,取两个值,返回一个bool.比如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)]
Run Code Online (Sandbox Code Playgroud)
这很好,因为你可以将它与struct和class一起使用,你甚至可以扩展它以与nils进行比较.
仍然,强烈建议阅读原始文章.它有更多的细节和很好的解释.
| 归档时间: |
|
| 查看次数: |
33673 次 |
| 最近记录: |