A Swift package that provides a convenient way to sort sequences using multiple key paths and custom sort descriptors, including both returning a new sorted array and in-place sorting.
MultiKeyPathSort
simplifies sorting collections by allowing you to define multiple sorting criteria using key paths. It supports sorting by both optional and non-optional properties, allows you to specify the sorting order (ascending or descending), and control how nil
values are handled. The package now also includes an in-place sort function for mutable collections.
- Sort by Multiple Key Paths: Define multiple levels of sorting using an array of sort descriptors.
- Optional Property Sorting: Easily sort optional properties with control over
nil
value placement. - Custom Sorting Order: Specify ascending or descending order for each sort criterion.
- In-Place Sorting: Sort mutable collections in place without creating a new array.
- Type-Safe and Flexible: Leverages Swift's type system and key paths for safe and flexible sorting.
Add MultiKeyPathSort
to your project using Swift Package Manager:
- In Xcode, go to File > Add Packages.
- Enter the repository URL:
https://github.com/yourusername/MultiKeyPathSort.git
. - Choose the package and add it to your project.
import MultiKeyPathSort
Create a model that you want to sort. For example:
struct Person {
let name: String
let age: Int
let height: Double?
}
Define sorting criteria using SortDescriptor
:
let sortByAge = SortDescriptor(\Person.age, ascending: true)
let sortByHeight = SortDescriptor(\Person.height, ascending: true, nilFirst: true)
Use the sorted(by:)
method on your sequence:
let people = [
Person(name: "Alice", age: 30, height: 5.5),
Person(name: "Bob", age: 25, height: nil),
Person(name: "Charlie", age: 35, height: 6.0)
]
let sortedPeople = people.sorted(by: [sortByAge, sortByHeight])
If you have a mutable collection, you can sort it in place:
var people = [
Person(name: "Alice", age: 30, height: 5.5),
Person(name: "Bob", age: 25, height: nil),
Person(name: "Charlie", age: 35, height: 6.0)
]
people.sort(by: [sortByAge, sortByHeight])
When sorting optional properties, you can specify where nil
values should be placed:
nilFirst = true
:nil
values will appear before non-nil
values.nilFirst = false
:nil
values will appear after non-nil
values.
Set ascending
to false
to sort in descending order:
let sortByAgeDescending = SortDescriptor(\Person.age, ascending: false)
import MultiKeyPathSort
struct Person {
let name: String
let age: Int
let height: Double?
}
var people = [
Person(name: "Alice", age: 30, height: 5.5),
Person(name: "Bob", age: 30, height: nil),
Person(name: "Charlie", age: 25, height: 6.0),
Person(name: "Diana", age: 25, height: 5.8)
]
let sortByAge = SortDescriptor(\Person.age)
let sortByHeight = SortDescriptor(\Person.height, ascending: true, nilFirst: true)
// In-place sorting
people.sort(by: [sortByAge, sortByHeight])
for person in people {
print("\(person.name): Age \(person.age), Height \(person.height ?? 0)")
}
Output:
Diana: Age 25, Height 5.8
Charlie: Age 25, Height 6.0
Bob: Age 30, Height 0.0
Alice: Age 30, Height 5.5
A structure representing a sort descriptor that defines sorting criteria for elements of a sequence.
-
Non-Optional Property
init<T: Comparable>(_ keyPath: KeyPath<Element, T>, ascending: Bool = true)
keyPath
: The key path to the property to sort by.ascending
: A Boolean value indicating whether the sort is ascending (true
) or descending (false
).
-
Optional Property
init<T: Comparable>(_ keyPath: KeyPath<Element, T?>, ascending: Bool = true, nilFirst: Bool = true)
keyPath
: The key path to the optional property to sort by.ascending
: A Boolean value indicating whether the sort is ascending (true
) or descending (false
).nilFirst
: A Boolean value indicating whethernil
values should appear first (true
) or last (false
).
-
Sorted by Descriptors
func sorted(by descriptors: [SortDescriptor<Element>]) -> [Element]
Returns a new array containing the sequence's elements sorted according to the given sort descriptors.
-
In-Place Sort by Descriptors
mutating func sort(by descriptors: [SortDescriptor<Element>])
Sorts the collection in place according to the given sort descriptors.
You can write tests using your preferred testing framework to ensure the sorting behaves as expected. Here's an example using Swift's XCTest
framework:
import XCTest
@testable import MultiKeyPathSort
final class MultiKeyPathSortTests: XCTestCase {
struct Person {
let name: String
let age: Int
let height: Double?
}
func testSortByMultipleKeyPaths() {
var people = [
Person(name: "Alice", age: 30, height: 5.5),
Person(name: "Bob", age: 30, height: nil),
Person(name: "Charlie", age: 25, height: 6.0),
Person(name: "Diana", age: 25, height: 5.8)
]
let sortByAge = SortDescriptor(\Person.age)
let sortByHeight = SortDescriptor(\Person.height, ascending: true, nilFirst: true)
people.sort(by: [sortByAge, sortByHeight])
let expectedOrder = ["Diana", "Charlie", "Bob", "Alice"]
XCTAssertEqual(people.map { $0.name }, expectedOrder)
}
}
Contributions are welcome! If you have ideas for improvements or have found a bug, please open an issue or submit a pull request.
-
Fork the repository on GitHub.
-
Clone your fork:
git clone https://github.com/yourusername/MultiKeyPathSort.git
-
Create a new branch:
git checkout -b feature/your-feature-name
-
Make your changes and commit:
git commit -am 'Add your message here'
-
Push to the branch:
git push origin feature/your-feature-name
-
Open a pull request on GitHub.
MultiKeyPathSort
is available under the MIT license. See the LICENSE file for more info.
For questions or suggestions, feel free to open an issue or contact me at [email protected].