🍎
ios.dev
ios.dev
  • 🍎ios.dev
    • 🛠️tools
    • 📰News
    • 📅Events
      • WWDC22
    • 🔲todo
      • 📖to read ...
      • 🧪研究中 ...
        • Formatter
        • Dependency Injection
        • 🔸Never
        • 🧪Google Sheet as JSON
      • 💈Examples
        • 💈MonthView
        • 💈CircleText
        • 💈Collapsible
      • 📓課程筆記
        • 📚SwiftUI Tutorials
          • Tutorials ⟩ Model
          • Tutorials ⟩ Views
          • Tutorials ⟩ Extensions
        • 📚Swift Animation Mastery
          • Triggers
            • 💈example ⟩ Picker
            • 💈example ⟩ SubButtons
        • Emoji Memory Game 📚
        • 設計卡片 📚
          • 排版
          • 元件化
          • 設定動畫
          • 設定手勢
          • 動畫的屬性
        • 新擬物風 (Neumorphic Design)
          • 漸層設計
            • Neu 🔸
          • 卡片設計
            • NeuCard 📦
          • 按鈕設計
            • IconCard 📦
            • ButtonUp, ButtonDown 📦
          • 設計進度條
            • NeuProgressBarTitle 📦
            • MyProgressBar2 📦
            • NeuProgressBar2 📦
      • 自訂型別
        • @KilometersPerHour
        • 🐶PlaygroundConsole
        • 👔EmojiTextFieldStyle
        • 🌅Outlined
        • 🌅BlurView
        • 🌅 SystemImage
        • 🌅 Fit
        • 🌅 Unwrap
        • ✨Custom Circle
    • 🔰templates
      • 🗒️page template
    • 🧪lab
    • 🔰terms
      • 🔰roles
        • 🔸implementer
        • 🔸user
      • 🔸state
      • 🔸data separation
      • 🔸data model
      • 🔸source of truth
      • 🔸user interaction
      • 🔸user interface
  • ⭐features
    • ⭐Swift
      • ⭐️ Swift 5.6
        • any
        • type placeholders
      • ⭐️ Swift 5.7
    • ⭐SwiftUI
      • ⭐SwiftUI 4.0
    • ⭐Xcode
      • Xcode 14
    • ⭐iOS
      • ⭐️ iOS 15
        • ⭐️ Live Text
      • ⭐️ iOS 16
    • Global Variables
    • Destructuring
    • Variadic Parameter
    • autoclosure
    • 🔰Key Path
      • Key Path Expressions as Functions
    • Calling Types As Functions
  • 👔custom
    • 🌀extension
      • 🌀View+
        • 👔view.if(_:then:)
        • 👔view.if(let:then:)
        • 👔view.inverseMask()
        • 👔view.overlayText()
        • 👔view.resizableFont()
        • 👔view.watermark()
        • 🚫view.foreground()
      • 🌀Color+
        • 🌀system colors
          • 🖼️SystemColorsView
          • 🖼️Swatch_0
        • 🌀Color.rgb()
        • 🌀color.hue
      • 🌀Gradient+
        • 🌀Gradient.linear()
        • 🌀Gradient + View
      • 🌀Image+
        • 🌀Image(playground:)
      • 🌀Formatter+
      • 🌀RoundedRectangle+
      • 🌀Text+
        • 🌀Text(symbol:)
    • 🔰snippets
      • 💾view + previews
      • 💾view modifier (template)
    • 🎁package
      • 📕如何自製 Swift package
      • 🏛️GeometryKit
        • 🅿️MetricSpace
        • 🅿️Vector
        • 🅿️ComplexNumber
        • 🅿️Vector2D
        • 🅿️Frame
    • 🅿️protocol
      • 🅿️Repeatable
    • 🖼️view
      • 👔HLine / VLine
      • 👔Swatch
      • 👔ShapeStyleView
    • 🎛️control
      • 🎛️SlidersForSize
      • 🎛️SliderWithLabel
  • 🐦Swift
    • 🎲math functions
      • abs()
      • .rounded()
      • .squareRoot()
      • pow()
    • 🔰scope
      • 🔸global scope
      • 🔸local scope
      • 🔸module
      • 🔹import
      • 🔸framework
        • 🧩built-in frameworks
          • 🧩Foundation
            • 📦Calendar
            • 📅Date
              • .sevenDaysOut
              • Date.from(year:month:day:)
              • Date.roundedHoursFromNow(hours)
            • 📦URL
              • 🌀 URL+ext
              • .isImage
            • 🗃️FileManager
              • 🌀 FileManager+ext
              • .documentDirectory
            • 🎁Bundle
            • 🅿️FormatStyle
          • 🧩Core Graphics
            • 🌀CGRect
              • rect.inset(by:)
            • 🌀CGSize
            • 🌀CGVector
            • 🌀CGPoint
          • 🧩SpriteKit
          • 🧩Combine
            • 🅿️Publisher
              • 🐶Future
              • 📦URLSession.DataTaskPublisher
            • ⚙️ Promise
            • 🔸Result
            • ⏰Timer
              • ✨cancellable timer
              • 👔TimerView
              • 👔TimerView (scheduledTimer)
          • 🧩AVFoundation
          • 🧩Swift Charts
          • 🧩Metal
          • 🧩WeatherKit
    • ➕operator
      • 🔸compound assignment operator
      • ✖️Character * Int
      • ✖️a^^n
      • ➕ternary conditional operator (a ? b : c)
    • 🍀type
      • 🍀type category
        • 🍀basic types
          • ✅Bool
            • .iOS14
          • ⁉️Optional
            • optional binding
            • optional chaining
            • nil coalescing
            • compare with Optional
          • 🔤String
            • 🔰String Interpolation
              • Custom String Interpolation
              • Expressible by String Interpolation
              • specifier / formatter
            • 📦AttributedString
            • 😃Emojis
            • String.Index
            • str.capitalized
            • str.split()
            • str.trim()
            • str[i], str[i..<j], str[i...j]
            • str.pad()
          • 📦Int
          • 📦Array
            • 🌀arr.split(size:)
            • 🌀arr.first()
            • arr.split(where:)
            • arr.index(of:)
            • arr.allElementsEqual
            • .allElementsSameLength
          • 📦enum
            • ⭐Comparable Enums
            • compound cases
            • 💡filter cases
            • 💡用 enum 封裝不同型別
          • 🚥tuple
          • 🔰function
            • 🔸default parameter
            • 🔸argument label
            • 🔸return value
              • #️discardable result
            • 🔰function as variable
            • 🔰fully qualified name
          • 🔰closure
            • 🔰closure expression
            • 🔰escaping closure
          • 🍄class
            • 🔰weak self
          • 🔢Numbers
            • 🅿️FloatingPoint
              • 🌀floating +−⨉÷ int
              • .decimalPlaces()
            • 🅿️BinaryFloatingPoint
              • .binaryBitPattern
              • 🖼️BinaryBitPatternView
            • 🅿️BinaryInteger
              • .binary(digits:)
            • range.contains()
          • ⚖️struct vs. class
        • 🅿️protocol
          • 🔰protocol inheritance
            • 🔰protocol hierarchy
            • 🚫can't change builtin hierarchy
          • 🔰protocol requirements
          • 🔴Protocol Extensions
          • 🔰generic protocol
            • 📘some, any
          • 🔰associated type
          • 🔴Conditional Conformance
          • 🔴Combining Protocols
          • 🔰Typecasting with Protocols
          • ⚠️retroactive conformance
          • 🅿️Any
          • 🅿️Comparable
            • .clamped(in:)
          • 🅿️Hashable
          • 🅿️Identifiable
          • 🅿️CaseIterable+ext
          • 🎬POP 介紹
          • ExpressibleByLiteral
        • ⚖️some╱any╱generics
          • 🔸some (opaque type)
          • 🔸any (boxed protocol type)
        • 🍀nested types
          • extension of nested types
          • ❓to nest or not to nest❓
        • 🍀generics
          • ⚠️generics & subtypes
          • ❗specialize generic function
          • ❓default type parameter
        • 🍀non-nominal types
      • 🔰property
        • 🔸public property
        • 🔸stored property
          • 🔸property observer (willSet/didSet)
        • 🔰computed property (get/set)
          • local computed variables
          • mutating getter / nonmutating setter
        • ⚖️stored vs. computed
        • ⚖️willSet/didSet vs. get/set
        • 🔰property wrapper
          • ✨Clamped
          • ✨Trimmed
          • ✨UnitInterval
      • 🐣inheritance
        • 🔸Self
      • 🔰type alias
      • 🔰extension
        • extension of typealias❓
      • ⭐implicit return
      • 🔰type erasure
      • 🔰initialization
        • 🔰initializers
          • 🔸required initializer
        • 🔰class initializer inheritance
          • 兩階段初始化
          • 自動繼承初始化程序
    • 🔰statement
      • 📘do
      • 🔤semicolon (;)
      • 🔰simple statement
        • 🔰expression
          • 🔰primary expressions
        • 🔰declaration
      • 🔰compiler control statement
    • 🔰flow control
      • 🔄loop
        • 📘for...in
        • 🔰for...in vs. forEach
      • 🔀branch
        • 📘if
        • 📘guard
        • 🔹switch
      • ↩️control transfer
        • 📘break
        • 📘continue
        • 📘return
        • 📘throw
        • 📘fallthrough
    • 🔰Collections
      • 🅿️Sequence
        • seq.forEach()
        • seq.reduce(_:_:)
        • seq.reduce(into:_:)
        • reduce() vs. reduce(into:)
        • seq.grouped(by:)
        • seq.sorted()
        • seq.sorted(by:)
        • seq.sorted(_:)
        • seq.foldMap(_:_:)
        • seq.stateMap(_:_:)
        • seq.sum
      • 🅿️Collection
        • collection[safe: index]
        • collection.allElementsEqual
        • collection.allElementsSameLength
        • collection.lengthsOfElements
        • collection.columnWidths
      • 🅿️IteratorProtocol
      • 🅿️OptionSet
      • Collection Types
    • 🔰Subscripts
      • Subscripts with Default Arguments
    • 🔰Attributes
      • ⭐Result Builders
        • result-building methods
        • various result types
        • result builder transform
        • custom result-builder attributes
    • 🔰Pattern Matching
      • 🔴Sentence Patterns
        • ... as! ...
        • (... as? ...)?.method()
        • if let ... as?
        • if/guard case let
        • for case let ... where
        • switch case
        • switch case is ...
        • switch case let ... where
        • switch case let ... as
        • switch on other types
      • operator (~=)
      • enum case pattern
    • 🔰Exceptions
    • 🏛️Standard Library
      • 🅿️Codable
    • 🔰Concurrency
      • 🅿️Sendable
        • 🐞has non-Sendable type
    • 🔰File System
    • 🔰Networking
    • 🐞Debugging
      • 🔮Mirror
        • Mirror.handleChildren()
      • 👔Logger
      • 👔HasMirrors
      • 👔log()
      • 👔CaseReflectable
      • 👔NonNominalTypeWrapper
      • Metatype
    • 🔰Input/Output
    • 🔰Regex
    • 🔰Access Control
  • 🔰SwiftUI
    • 🔰SwiftUI terms
      • 🔸built-in view
    • 🔰introduction
      • SwiftUI Essentials
    • 🚩app
      • 🅿️App
      • 🔰scenes
        • 🅿️Scene
      • 🔰windows
        • 📦WindowGroup
    • 🔰views
      • 🔰view hierarchy
        • 🔸container view
        • 🔸composed view
        • 🔸root view
        • 🔸child view
        • 🔸parent view
        • 🔸destination view
      • 🔰view state
        • 🔸state value
          • @State
            • 🔰init State
            • ✨dynamic underline
        • 🔸state object
          • @StateObject
        • 🔸observable object
          • 🅿️ObservableObject
          • 🔸published value
          • 🔸observer
          • @Published
        • 🔰animate state changes
        • 🔸binding
          • @Binding
            • 🔰Binding State
            • 🔰Binding from StateObject
            • 🔰init Binding
            • ❗return Binding
      • 🔰view layout
        • 🔹.padding( )
        • 🔴Position
        • 🔸frame
          • 🌀view + .frame()
          • 🔹.frame()
            • ✨TestIdealSizeView
            • ✨TestFrameView
        • 🔸view size
          • 🔴Size Classes
          • 🔴Dynamic Type Sizes
            • 🌀DynamicTypeSize+ext
          • 🔸ideal size
        • 🎁stacks
          • 📦ZStack
          • 👔AdaptiveHStack
          • 👔StackForEach
        • 🎁grids
          • 📦LazyVGrid
            • 🖼️StandardGradientGrid
          • 🟠grid layout algorithm
          • 📦GridItem
            • 🔰adaptive column
            • 🔰flexible column
            • ✨NColumns
          • 👔VGridForEach
            • ✨TestLittleSquares
          • 👔ScrollVGridForEach
            • ✨TestSVGFE
          • ✨Grids ⟩ examples
            • ✨Smileys
            • ✨Grid
              • 📦GridLayout
            • ✨MyGrid
              • ⛔Generic parameter '...' could not be inferred.
            • 🐞problem with .readSize()
            • ✨ItemsView
              • 🖼️TestItemsView
              • 📦RatioRetainingLayout
              • 🎛️SizeRatioControl
              • 🅿️IndexedGridLayout
        • Adaptive Layout
        • Alignment
          • Implicit Alignment
          • Custom Alignment within ZStack
          • 🔰各種對齊 (alignment)
        • 🌿Layout ⟩ Types
          • 📦TimelineView
        • ⛔Cannot convert value of type 'Self' to expected argument type 'Binding<C>'
      • 🔰view actions
        • 🔰delete item from list
        • 🔰add item to list
      • 🔰view environment
        • 🔸environment object
        • 🔸environment value
          • 📦dismiss
        • @Environment
          • .editMode
        • @EnvironmentObject
      • 🔰view groupings
        • 🖼️ForEach
          • 🔹.onDelete
          • ⚖️List vs. ForEach
          • ✨ForEach examples
      • 🔰navigation
        • 🔸navigation bar
        • 📦NavigationStack
        • 📦NavigationSplitView
        • 📦NavigationLink
      • 🔰view modifier
        • ✨examples
          • .nsfw()
          • .roundedBorder()
          • .neumorphic()
        • 🔰add modifiers to Xcode
        • 🔰conditional modifier
        • 🔰layout modifier
        • 🔰rendering modifier
        • 🔰adjusting text
        • 🅿️ViewModifier
      • 🔰drawing views
        • 🔸foreground
          • 🔸foreground element
          • 🔸foreground style
            • 🔹view.foregroundStyle()
            • 🔹ShapeStyle.foreground
        • 🔸background
          • 🔸background style
          • 🔹.background()
        • 🔸blend mode
          • ✨.destinationOut
        • 🔸mask
          • 🔹view.mask()
        • 🔸shadow
          • 🔰container + shadow
          • 📦ShadowStyle
          • 🔹view.shadow()
        • 🔹view.clipShape()
          • 🐞mismatching types
        • 🔹veiw.compositingGroup()
      • 🔰measuring views
        • 📦GeometryProxy
          • 🌀GeometryProxy+ext
        • 📦 GeometryReader
          • 🔰GeometryReader 的對齊方式
      • 🔰Configuring Views
      • 🔰events
        • 🔹.allowsHitTesting()
      • 📦ViewBuilder
        • ViewBuilder transforms
        • support for stored properties
      • 🔰Optional Views
      • 🅿️View
        • .dimension()
          • 🌀Path+
          • 👔DimensionPositions
          • 👔WidthDimension
          • 👔HeightDimension
          • 👔LeftArrow
          • 👔LeftArrowShape
          • 👔UpArrow
          • 👔UpArrowShape
        • id(_:)
        • .badge()
        • .border(), .borderLeft() ...
        • .hideIf()
        • .logType()
        • .getSize()
        • .grids()
        • .offset()
        • .onChangeSize()
        • .onDrag
        • ❌.readSize()
        • resizableFont()
        • .show(if:)
        • .shadowedBorder()
        • .testFrame()
        • view.actOnSelfWidth()
      • 📦Link
      • 🔰Swipe Actions
      • 🔰3D transform
      • ⭐Live Activites
      • 🔰life cycle
    • 🔰styles
      • 🔸current style
    • 🔰lists
      • 📦List
        • 🅿️ListStyle
        • ⭐list background color
    • 🎛️controls
      • 🔰pickers
        • 🎛️Picker
        • 🎛️DatePicker
      • 🔰toolbar
      • 🔰FocusState
      • 📦Divider
      • 📦Text
        • 📦Font
          • 🔰custom font
        • 🔰text formats
          • 🔸no wrap
        • 🔰Markdown
        • ❓math equations❓
        • ⭐number of lines
      • 📦Image
        • 🔹image.renderingMode()
        • 🔸template image
        • 🔰SF Symbols
        • 🍄ImageRenderer
      • 📦Label
        • Label Styles
          • Label ⟩ custom styles
      • 📦GroupBox
      • 📦ControlGroup
      • 📦Section
      • 📦ScrollView
      • ⭐ShareLink
      • 🎛️Button
        • 🔰Button Roles
        • 🔰Button Styles
          • 🅿️PrimitiveButtonStyle
          • 🔰Button with Materials
          • Button ⟩ custom styles
      • 🎛️EditButton
      • 🎛️ProgressView
      • 🎛️TextField
        • TextField ⟩ formats
          • ✨.currency
        • TextField ⟩ styles
          • textField.style(_:)
      • 🎛️Toggle
        • 🔰Toggle Styles
      • 🎛️SecureField
      • 🎛️Slider
        • 🔰Slider label position
      • 🎛️Gauge
    • 🔰shapes
      • 🔰transform shape
        • 🔹shape.stroke()
          • 🌀StrokeStyle
          • ✨MarchingAnts
      • 💈正圓變成正五邊形
      • 🅿️Shape
        • 📦AnyShape
        • 🔸fill
          • ✨even-odd fill mode
          • .evenOddFill
        • 🌀Shape + ext
          • shape.outlined()
        • ✨RoundedCorners
        • ✨Helper Shapes
          • 💜LineShape
          • 💜GridLinesShape
          • 🖼️GridLines
          • 🖼️Line
          • 🖼️Pin
          • 🖼️Point
        • ✨Sports Car in Sunset
          • 💜Vehicle.CarBodyShape
          • 🖼️Vehicle
        • 👔Polygon
      • 🅿️ShapeStyle
        • 📦Color
          • 🔸color.gradient
          • 🔰Theme
        • 📦Gradient
        • 📦ImagePaint
        • ♦️.shadow()
        • .white()
        • ShapeStyle+LinearGradient
        • 📦Material
      • 🔰Matched Geometry Effect
        • ✨MGE ⟩ animation
      • 📦Path
        • path.fit()
        • path.line()
        • path.transform
      • 📦Canvas
      • 📦Anchor<T>
    • 🎁container
      • 🎁collection
      • 🎁layout
        • ⭐Grid
        • ⭐ViewThatFits
        • ⭐AnyLayout
        • ⭐Layout
      • 🎁presentation
        • ⭐bottom sheet
        • 🎁TabView
          • ✨tabs with tag
    • 🚦data flow
      • 🚥Preferences
        • 👔ViewPreference
        • 🅿️PreferenceKey
          • FirstNonNil<T>
          • AllValues<T>
          • MaxValue<T>
        • 🚥Anchor Preferences
        • ✨Preferences ⟩ examples
          • ✨views with same width
            • .reportWidth() ...
          • ✨selected button with underline
    • 🏎️animations
      • 🔰implicit vs. explicit
      • 🅿️Animatable
        • 🅿️GeometryEffect
        • 🔸Animatable Modifiers
          • ✨.tapToShake()
      • 📦Animation
      • 🔰delay
      • 🔸Animation Curves
        • 🔸Bezier Curves
        • easeInOutExp
      • ✨Animations ⟩ examples
        • ✨Spinner
        • ✨Wheel of Fortune
        • ✨Loading Indicators
      • 🔰Transitions
        • 🔰custom transitions
        • 📦AnyTransition
          • AnyTransition.combined()
        • ✨transition ⟩ scale
        • ✨combining transitions
    • 👆Gestures
      • 🔰update gesture states
      • 🔰update view states
      • 🚥GestureState
      • 👆Long Press
      • 👆Drag
        • 👔.draggable()
          • 🖼️HandlePoint
      • 🔰Combining Gestures
        • 👆.sequenced()
          • ✨long press + drag
    • 🔰Presentations
      • 🔰Launch Screen
      • 🔰External Screens
      • 🔰Refreshable Views
      • 🔰Alert
      • 🎛️Confirmation Dialog
    • 🔰Debugging in SwiftUI
    • 🎹keyboard
    • 🔰for mac
    • 🚫deprecated
      • 🎁NavigationView
        • .navigationViewStyle(_:)
  • 🎛️widgets
  • 🎁Data Structures
    • Graph
      • Tree
  • 🧠Algorithms
    • Searching
      • Binary Search
    • Hashing
    • Sorting
      • sort using key paths
      • ranking list
      • Merge Sort
    • Filtering
    • Path Finding
      • A*
  • ⛱️Swift Playgrounds
    • Playground Book
      • Structuring Content
    • 檔案結構
      • 💾Manifest.plist
    • 使用模組
    • 📚Learn to Code 2
      • 型別
        • 🔸CharacterName
        • 🍄NodeWrapper
        • 👔ItemID
        • 🅿️Learn to Code 2 ⟩ Animatable
        • 🅿️Item
        • 🍄Actor
        • 🍄Expert
        • 🍄Portal
      • 關卡
        • Variables
          • 檢查相等值
          • 計算開關數量
          • 收集總數
        • Types
          • 開啟與關閉傳送門
          • 關閉傳送門
        • 創造世界
    • 編碼社群
    • Swift Playgrounds Subscription
  • 🥁Audio / Video
    • .speak()
    • Language Tags
  • ⛔Errors
    • ⛔CompileDylibError: Failed to build XXX.swift
    • ⛔Fatal error: String index is out of bounds
    • ❌Internal error: missingPackageDescriptionModule
    • ❌Argument type 'xxx' does not conform to expected type '_FormatSpecifiable'
    • ⛔️ escaping closure captures mutating 'self' parameter
    • ❓iPad/iPhone is Busy: Fetching debug symbols for ...
    • ❓Class _PointQueue is implemented in both ...
    • ⛔Protocol '...' can only be used as a generic constraint because it has Self or associated type
    • ⛔non-nominal type `...` cannot be extended
    • ⛔cannot assign value of type 'T' to type 'T'
  • 附錄
    • 🧩Swift Package
      • Package.swift
      • version number
      • Swift Package Index
      • 3rd party packages
    • 🔰Design Patterns
      • MVC
      • MVVM
      • DIP
    • 🛠️Xcode
      • 🔰build schemes
      • 🔧Instruments
      • 🤖Compiler Directives
        • @available, #available
        • #if ... #else ... #endif
        • #file, #line, #function
      • 🎹shortcuts
      • 🔰Documentation
        • 🔰DocC
      • Source Control
        • Merge
      • Preview
        • Preview Devices
      • App Icon
      • Simulator
    • 💼Projects
    • ✅Testing System
      • ✅Swift Testing
      • ✅XCTest
        • XCTAssertEqual (==)
        • XCTUnwrap
      • Fixtures
    • Prototyping
    • 💡Tips
      • iOS version
      • Swift version
    • JSON
      • Load JSON
    • Terms
      • reachability
    • 其他
      • 待解問題❓
      • 測試用 🧪
      • 關注對象
      • 重要語錄
      • 圖例
    • 雜記 📥
      • 📦Measurement
      • Replicating Types in Swift
      • 字型專有名詞
      • State Machine
Powered by GitBook
On this page

Was this helpful?

  1. Swift
  2. Collections
  3. Sequence

seq.sorted(_:)

sort a sequence on multiple properties

Previousseq.sorted(by:)Nextseq.foldMap(_:_:)

Last updated 3 years ago

Was this helpful?

有了下面的擴充後,我們就可以用這麼漂亮的語法來對物件排序:

let sorted = items.sorted(
    .descending(\.price), 
    .descending(\.rate), 
    .ascending (\.title)
)

善用 guard ... else 可以讓「用多個屬性來排序」的語法變得更簡潔。

// 1.0.0: 2021.12.11

/*
    設計理念:
    --------
    我們將要用於排序的屬性 key path (`prop`) 傳入 `SortOrder`
    的 `isBefore` 與 `isEqual` 兩個 closure 中,如此就可以
    直接用 order.isBefore(a, b) 或 order.isEqual(a, b) 來
    比較兩個 Element 的大小,而不需要為了要將這個 key path 存起來
    而傷腦筋到底要用: KeyPath<Element, Property> 還是要用
    PartialKeyPath<Element> 型別?

    事實上,用這兩種型別存起來都有問題。

    首先,如果用 KeyPath<Element, Property>,這時勢必要將 SortOrder
    的型別也改為 SortOrder<Element, Property>,但這樣一改,後面
    sorted() 函數的 variadic 參數 orders 也要跟著改,這會造成
    所有的屬性都是同一種 Property 型別,直接讓 sorted() 函數無法使用。

    再來,如果用 PartialKeyPath<Element> 來存 key path,這時
    這個 key path 會失去 (type-erase) 它的 Value type,間接
    造成系統無法辨別 element[keyPath: keyPath] 的型別,因此也
    不知道原來這個值是一個 Comparable,所以在程式中也沒辦法用 "==",
    "<", ">" 這些運算符號,因此就算將這個 key path 存起來也沒什麼用。
*/

/// SortOrder
public struct SortOrder<Element> {
    public let isBefore: (Element, Element) -> Bool
    public let isEqual : (Element, Element) -> Bool
}

// ⭐️ SortOrder
extension SortOrder {

    /// .ascending(\.property)
    public static func ascending<Property: Comparable>(
        _ prop: KeyPath<Element, Property>
    ) -> SortOrder<Element> 
    {
        return SortOrder(
            isBefore: { $0[keyPath: prop] <  $1[keyPath: prop] },
            isEqual : { $0[keyPath: prop] == $1[keyPath: prop] }
        )
    }
    
    /// .descending(\.property)
    public static func descending<Property: Comparable>(
        _ prop: KeyPath<Element, Property>
    ) -> SortOrder<Element> 
    {
        return SortOrder(
            isBefore: { $0[keyPath: prop] >  $1[keyPath: prop] },
            isEqual : { $0[keyPath: prop] == $1[keyPath: prop] }
        )
    }
}

// seq.sorted(_:)
extension Sequence {
    /// ⭐️ items in a `Sequence` sorted by multiple properties.
    /// example:
    /// ```
    /// items.sorted(.ascending(\.title))
    /// items.sorted(.ascending(\.price), .descending(\.rate))
    /// ```
    public func sorted(_ orders: SortOrder<Element>...) -> [Element] {
        return sorted { a, b in
            // ⭐️ if some property is not equal, return the order
            for order in orders {
                guard order.isEqual(a, b) 
                else { return order.isBefore(a, b) }
            }
            // ⭐️ all properties are equal, so `a` is NOT before `b`
            return false
        }
    }
}
// Using extension
struct Post {
    let title: String
    let views: Int
    let duration: Int
}

var posts = [
    Post(title: "Alice", views: 1, duration:  3),
    Post(title: "Beth" , views: 1, duration:  1),
    Post(title: "Cloe" , views: 1, duration:  2),
    Post(title: "David", views: 5, duration:  2),
    Post(title: "Ethan", views: 4, duration: 10),
    Post(title: "Felix", views: 5, duration:  2)
]

// ⭐️ seq.sorted(_:)
let sorted = posts.sorted(
    .descending(\.views), 
    .descending(\.duration), 
    .ascending (\.title)
)
// [
//     Post(title: "David", views: 5, duration:  2), 
//     Post(title: "Felix", views: 5, duration:  2), 
//     Post(title: "Ethan", views: 4, duration: 10), 
//     Post(title: "Alice", views: 1, duration:  3), 
//     Post(title: "Beth" , views: 1, duration:  2), 
//     Post(title: "Cloe" , views: 1, duration:  1)
// ]
// Without using extension
struct Post {
    let title: String
    let views: Int
    let duration: Int
}

var posts = [
    Post(title: "Alice", views: 1, duration: 3),
    Post(title: "Beth", views: 1, duration: 2),
    Post(title: "Cloe", views: 1, duration: 1),
    Post(title: "David", views: 5, duration: 2),
    Post(title: "Ethan", views: 4, duration: 10),
    Post(title: "Felix", views: 5, duration: 2)
]

// ------------ main ------------

// sort in place
posts.sort {  // $0 is before $1 ?
    
    // check on `views` first
    guard $0.views == $1.views else {
        return $0.views > $1.views          // decreasing
    }

    // $0.views == $1.views
    // check on `duration` next
    guard $0.duration == $1.duration else {
        return $0.duration > $1.duration    // decreasing
    }

    // $0.views    == $1.views
    // $0.duration == $1.duration 
    // check on `title` last
    return  $0.title < $1.title             // increasing
}

print(posts)
// [
//     Post(title: "David", views: 5, duration: 2), 
//     Post(title: "Felix", views: 5, duration: 2), 
//     Post(title: "Ethan", views: 4, duration: 10), 
//     Post(title: "Alice", views: 1, duration: 3), 
//     Post(title: "Beth" , views: 1, duration: 2), 
//     Post(title: "Cloe" , views: 1, duration: 1)
// ]
  • Key Path

  • sort using key paths

Sorting Swift Collections ⟩ ⭐️

Web Dev N⟩otes ⟩

KeyPathKit ⟩ , ⭐️⭐️⭐️

🐦
🔰
🅿️
👉
replit
👉
replit
Sorting on multiple properties
The power of key paths in Swift
Sort array of objects by multiple properties with Swift Tuple
How to sort by multiple properties in Swift
sort by multiple keys
.sort(by:)
source code