How to sort an array of structures using multiple values?

I already have code to sort by 1 value, as shown below, but I'm wondering how to sort multiple values? I would like to sort by set and then by someString.

One is an integer, in which case the string is a string. I considered converting an integer to a string and then combining them, but thought there should be a better way, because I can have 2 integers to sort in the future.

struct Condition { var set = 0 var someString = "" } var conditions = [Condition]() conditions.append(Condition(set: 1, someString: "string3")) conditions.append(Condition(set: 2, someString: "string2")) conditions.append(Condition(set: 3, someString: "string7")) conditions.append(Condition(set: 1, someString: "string9")) conditions.append(Condition(set: 2, someString: "string4")) conditions.append(Condition(set: 3, someString: "string0")) conditions.append(Condition(set: 1, someString: "string1")) conditions.append(Condition(set: 2, someString: "string6")) // sort let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in return (lhs.set) < (rhs.set) } // printed sorted conditions for index in 0...conditions.count-1 { println("\(sorted[index].set) - \(sorted[index].someString)") } 
+6
source share
3 answers

I don't own Swift yet, but the basic idea for multiple criteria selection is:

 let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in if lhs.set == rhs.set { return lhs.someString < rhs.someString } return (lhs.set) < (rhs.set) } 
+9
source

Despite the fact that the previous answers in the requested case are perfectly fine, I would like to make a more general approach for this:

 infix operator &lt=> { associativity none precedence 130 } func &lt=> &ltT: Comparable>(lhs: T, rhs: T) -> NSComparisonResult { return lhs &lt rhs ? .OrderedAscending : lhs == rhs ? .OrderedSame : .OrderedDescending } private func _sortedLexicographically&ltS: SequenceType&gt(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] { return sorted(source, { lhs, rhs in for compare in comparators { switch compare(lhs, rhs) { case .OrderedAscending: return true case .OrderedDescending: return false case .OrderedSame: break } } return false }) } public func sortedLexicographically&ltS: SequenceType>(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] { return _sortedLexicographically(source, comparators) } extension Array { func sortedLexicographically(comparators: [(Element, Element) -> NSComparisonResult]) -> [Element] { return _sortedLexicographically(self, comparators) } } 

from here it is quite easy to place an order, as requested:

 struct Foo { var foo: Int var bar: Int var baz: Int } let foos = [Foo(foo: 1, bar: 2, baz: 3), Foo(foo: 1, bar: 3, baz: 1), Foo(foo: 0, bar: 4, baz: 2), Foo(foo: 2, bar: 0, baz: 0), Foo(foo: 1, bar: 2, baz: 2)] let orderedFoos = foos.sortedLexicographically([{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }]) 

If this type of comparison for this type is internal to the type itself, and not to one sort only in place, you need to follow a more stdlib-like approach and extend Comparable instead:

 extension Foo: Comparable {} func == (lhs: Foo, rhs: Foo) -> Bool { return lhs.foo == rhs.foo && lhs.bar == rhs.bar && lhs.baz == rhs.baz } func &lt (lhs: Foo, rhs: Foo) -> Bool { let comparators: [(Foo, Foo) -> NSComparisonResult] = [{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }] for compare in comparators { switch compare(lhs, rhs) { case .OrderedAscending: return true case .OrderedDescending: return false case .OrderedSame: break } } return false } let comparableOrderedFoos = sorted(foos) 

There would be another possible approach that the LexicographicallyComparable protocol makes, which says which Comparable fields and in which priority they have, but, unfortunately, I cannot think of a way to do this without using generic generics that are not supported in Swift like 2.0, while maintaining the type of security typical of Swift code.

+6
source

You would compare someString if the set values ​​were the same, otherwise use the existing comparison:

 let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in if lhs.set == rhs.set { return lhs.someString < rhs.someString } else { return lhs.set < rhs.set } } 
+4
source

Source: https://habr.com/ru/post/978459/


All Articles