645

Let's say we have a custom class named imageFile and this class contains two properties:

class imageFile  {
    var fileName = String()
    var fileID = Int()
}

Lots of them are stored in an Array:

var images : Array = []

var aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 101
images.append(aImage)

aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 202
images.append(aImage)

How can I sort the images array by 'fileID' in ascending or descending order?

TylerH
  • 20,799
  • 66
  • 75
  • 101
modusCell
  • 13,151
  • 9
  • 53
  • 80

20 Answers20

1126

First, declare your Array as a typed array so that you can call methods when you iterate:

var images : [imageFile] = []

Then you can simply do:

Swift 2

images.sorted({ $0.fileID > $1.fileID })

Swift 3

images.sorted(by: { $0.fileID > $1.fileID })

Swift 5

images.sorted { $0.fileId > $1.fileID }

The example above gives the results in descending order.

jxxe
  • 234
  • 1
  • 4
  • 12
Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • In case it helps anybody that finds this answer: I found this answer independently by consulting the Swift Standard Library Reference — https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/SwiftStandardLibraryReference.pdf – Tommy Jul 14 '14 at 18:09
  • 13
    do you now need to do ```images.sortInPlace({ $0.fileID > $1.fileID })```? – Taylor M Sep 30 '15 at 21:21
  • can i add a third comparison into the same line? – cmario Dec 09 '15 at 16:22
  • It has been replaced by sorted function. – Kevin Jan 12 '17 at 08:16
  • Can I also use this to sort by comparisons, for example having the array sort by the days of the week? if so how? – Kristofer Nov 11 '19 at 19:02
  • @Kristofer Yes you can just use < operator without having to define $0 & $1, as they are same type and known type. – Rakshitha Muranga Rodrigo Nov 22 '20 at 21:14
281

[Updated for Swift 3 with sort(by:)] This, exploiting a trailing closure:

images.sorted { $0.fileID < $1.fileID }

where you use < or > depending on ASC or DESC, respectively. If you want to modify the images array, then use the following:

images.sort { $0.fileID < $1.fileID }

If you are going to do this repeatedly and prefer to define a function, one way is:

func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool {
  return this.fileID < that.fileID
}

and then use as:

images.sort(by: sorterForFileIDASC)
Cem Schemel
  • 442
  • 3
  • 13
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • how can i sue this with string? i need to sort string by its length – Muneef M Nov 28 '15 at 20:57
  • 1
    @MuneefM just return string1.length < string2.length – Surjeet Rajput Aug 30 '16 at 10:33
  • Can I also use this to sort by comparisons, for example having the array sort by the days of the week? if so how? – Kristofer Nov 11 '19 at 19:02
  • This might sound odd but ... isn't the comparison sign given in the `sorterForFileIDASC` function in this answer incorrect? The author themselves mentions using `<` for an ascending sort, but then uses `>` in `sorterForFileIDASC` which, as far as I can tell, is meant to be an ascending sort. I tried to propose an edit where I flipped the sign, but apparently edits have to be a minimum of 6 characters, so I can't even propose it. – Cem Schemel May 21 '21 at 16:02
  • @CemSchemel, it depends in which order to compare, right? – Vega May 26 '21 at 06:12
  • Yes, it does. But I thought the example function was trying to achieve ascending sort (hence the name `sorterForFileIDASC`) yet the actual comparison would've led to a descending sort. I've since proposed the change in an edit and it got accepted, so I don't think this concern is applicable anymore in the current version of this answer. – Cem Schemel May 26 '21 at 14:31
64

Swift 3

people = people.sorted(by: { $0.email > $1.email })
quemeful
  • 9,542
  • 4
  • 60
  • 69
62

With Swift 5, Array has two methods called sorted() and sorted(by:). The first method, sorted(), has the following declaration:

Returns the elements of the collection, sorted.

func sorted() -> [Element]

The second method, sorted(by:), has the following declaration:

Returns the elements of the collection, sorted using the given predicate as the comparison between elements.

func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]

#1. Sort with ascending order for comparable objects

If the element type inside your collection conforms to Comparable protocol, you will be able to use sorted() in order to sort your elements with ascending order. The following Playground code shows how to use sorted():

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted()
print(sortedImages)

/*
 prints: [ImageFile with ID: 100, ImageFile with ID: 200, ImageFile with ID: 300]
 */

#2. Sort with descending order for comparable objects

If the element type inside your collection conforms to Comparable protocol, you will have to use sorted(by:) in order to sort your elements with a descending order.

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0 > img1
})
//let sortedImages = images.sorted(by: >) // also works
//let sortedImages = images.sorted { $0 > $1 } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */

#3. Sort with ascending or descending order for non-comparable objects

If the element type inside your collection DOES NOT conform to Comparable protocol, you will have to use sorted(by:) in order to sort your elements with ascending or descending order.

class ImageFile: CustomStringConvertible {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0.fileID < img1.fileID
})
//let sortedImages = images.sorted { $0.fileID < $1.fileID } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */

Note that Swift also provides two methods called sort() and sort(by:) as counterparts of sorted() and sorted(by:) if you need to sort your collection in-place.

Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • If you want to sort by multiple properties, you can pass in a tuple ($.lhs.fileID, $.lhs.fileName) < ($.lhs.fileID, $.lhs.fileName).You can mix ordering with (lhs.val1, rhs.val2) > (rhs.val1, lhs.val2). Finally, if you want to have a custom sort that's different for ascending and descending (as I did where I wanted headers to always appear before content), you can provide a different overload for _static func >_ – green_knight Jun 16 '20 at 16:54
59

Nearly everyone gives how directly, let me show the evolvement:

you can use the instance methods of Array:

// general form of closure
images.sortInPlace({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })

// types of closure's parameters and return value can be inferred by Swift, so they are omitted along with the return arrow (->)
images.sortInPlace({ image1, image2 in return image1.fileID > image2.fileID })

// Single-expression closures can implicitly return the result of their single expression by omitting the "return" keyword
images.sortInPlace({ image1, image2 in image1.fileID > image2.fileID })

// closure's argument list along with "in" keyword can be omitted, $0, $1, $2, and so on are used to refer the closure's first, second, third arguments and so on
images.sortInPlace({ $0.fileID > $1.fileID })

// the simplification of the closure is the same
images = images.sort({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in image1.fileID > image2.fileID })
images = images.sort({ $0.fileID > $1.fileID })

For elaborate explanation about the working principle of sort, see The Sorted Function.

fujianjin6471
  • 5,168
  • 1
  • 36
  • 32
28

In Swift 3.0

images.sort(by: { (first: imageFile, second: imageFile) -> Bool in
    first. fileID < second. fileID
})
jaiswal Rajan
  • 4,641
  • 1
  • 27
  • 16
22

You can also do something like

images = sorted(images) {$0.fileID > $1.fileID}

so your images array will be stored as sorted

Pascal
  • 16,846
  • 4
  • 60
  • 69
picsoung
  • 6,314
  • 1
  • 18
  • 35
22

Swift 4.0, 4.1 & 4.2:

First, I created mutable array of type imageFile as shown below

var arr = [imageFile]()

Create mutable object image of type imageFile and assign value to properties as shown below

var image = imageFile()
image.fileId = 14
image.fileName = "A"

Now, append this object to array arr

arr.append(image)

Now, assign the different properties to same mutable object i.e image

image = imageFile()
image.fileId = 13
image.fileName = "B"

Now, again append image object to array arr

arr.append(image)

Now, we will apply Ascending order on fileId property in array arr objects. Use < symbol for ascending order

arr = arr.sorted(by: {$0.fileId < $1.fileId}) // arr has all objects in Ascending order
print("sorted array is",arr[0].fileId)// sorted array is 13
print("sorted array is",arr[1].fileId)//sorted array is 14

Now, we will apply Descending order on on fileId property in array arr objects. Use > symbol for Descending order

arr = arr.sorted(by: {$0.fileId > $1.fileId}) // arr has all objects in Descending order
print("Unsorted array is",arr[0].fileId)// Unsorted array is 14
print("Unsorted array is",arr[1].fileId)// Unsorted array is 13

In Swift 4.1 & 4.2, for sorted order use

let sortedArr = arr.sorted { (id1, id2) -> Bool in
  return id1.fileId < id2.fileId // Use > for Descending order
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Gurjinder Singh
  • 9,221
  • 1
  • 66
  • 58
20

Swift 2 through 4

The original answer sought to sort an array of custom objects using some property. Below I will show you a few handy ways to do this same behavior w/ swift data structures!

Little things outta the way, I changed ImageFile ever so slightly. With that in mind, I create an array with three image files. Notice that metadata is an optional value, passing in nil as a parameter is expected.

 struct ImageFile {
      var name: String
      var metadata: String?
      var size: Int
    }

    var images: [ImageFile] = [ImageFile(name: "HelloWorld", metadata: nil, size: 256), ImageFile(name: "Traveling Salesmen", metadata: "uh this is huge", size: 1024), ImageFile(name: "Slack", metadata: "what's in this stuff?", size: 2048) ]

ImageFile has a property named size. For the following examples I will show you how to use sort operations w/ properties like size.

smallest to biggest size (<)

    let sizeSmallestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size < next.size
    }

biggest to smallest (>)

    let sizeBiggestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size > next.size
    }

Next we'll sort using the String property name. In the same manner, use sort to compare strings. But notice the inner block returns a comparison result. This result will define sort.

A-Z (.orderedAscending)

    let nameAscendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedAscending
    }

Z-A (.orderedDescending)

    let nameDescendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedDescending
    }

Next is my favorite way to sort, in many cases one will have optional properties. Now don't worry, we're going to sort in the same manner as above except we have to handle nil! In production;

I used this code to force all instances in my array with nil property values to be last. Then order metadata using the assumed unwrapped values.

    let metadataFirst = images.sorted { (initial, next) -> Bool in
      guard initial.metadata != nil else { return true }
      guard next.metadata != nil else { return true }
      return initial.metadata!.compare(next.metadata!) == .orderedAscending
    }

It is possible to have a secondary sort for optionals. For example; one could show images with metadata and ordered by size.

jnblanchard
  • 1,182
  • 12
  • 12
19

Two alternatives

1) Ordering the original array with sortInPlace

self.assignments.sortInPlace({ $0.order < $1.order })
self.printAssignments(assignments)

2) Using an alternative array to store the ordered array

var assignmentsO = [Assignment] ()
assignmentsO = self.assignments.sort({ $0.order < $1.order })
self.printAssignments(assignmentsO)
Bernauer
  • 729
  • 9
  • 13
  • 3
    re 2) What is the point in constructing an empty array and discarding it in the very next line? I'd sugest using `var assignmentsO : [Assignment]` or combine it into one line by using `let assignmentsO = self.assignments.sort({ $0.order < $1.order })` – Hermann Klecker Jan 22 '16 at 23:35
  • 2
    Hi Hermann! There is a very thin line between writing readable and efficient code. In this case, the only point is making it more readable to the community ;) enjoy! – Bernauer Jan 24 '16 at 16:30
11

You return a sorted array from the fileID property by following way:

Swift 2

let sortedArray = images.sorted({ $0.fileID > $1.fileID })

Swift 3 OR 4

let sortedArray = images.sorted(by: { $0.fileID > $1.fileID })

Swift 5.0

let sortedArray = images.sorted {
    $0.fileID < $1.fileID
}
Vicky Prajapati
  • 137
  • 1
  • 5
11

If the array elements conform to Comparable, then you can simply use functional syntax:

array.sort(by: <)

If you're sorting based on a custom type, all you need to do is implement the < operator:

class ImageFile {
    let fileName: String
    let fileID: Int
    let fileSize: Int
    static func < (left: ImageFile, right: ImageFile) -> Bool {
        return left.fileID < right.fileID
    }
}

However, sometimes you don't want one standard way of comparing ImageFiles. Maybe in some contexts you want to sort images based on fileID, and elsewhere you want to sort based on fileSize. For dynamic comparisons, you have two options.

sorted(by:)

images = images.sorted(by: { a, b in
    // Return true if `a` belongs before `b` in the sorted array
    if a.fileID < b.fileID { return true }
    if a.fileID > b.fileID { return false }
    // Break ties by comparing file sizes
    return a.fileSize > b.fileSize
})

You can simplify the syntax using a trailing closure:

images.sorted { ... }

But manually typing if statements can lead to long code (if we wanted to break file size ties by sorting based on file names, we would have quite an if chain of doom). We can avoid this syntax by using the brand-new SortComparator protocol (macOS 12+, iOS 15+):

sorted(using:)

files = files.sorted(using: [
    KeyPathComparator(\.fileID, order: .forward),
    KeyPathComparator(\.fileSize, order: .reverse),
])

This code sorts the files based on their file ID (.forward means ascending) and breaks ties by sorting based on file size (.reverse means descending). The \.fileID syntax is how we specify key paths. You can expand the list of comparators as much as you need.

McKinley
  • 1,123
  • 1
  • 8
  • 18
  • 4
    This answer is the only one that highlights the power of `KeyPathComparator` and should be pinned. – parapote Feb 14 '23 at 04:50
9

If you are going to be sorting this array in more than one place, it may make sense to make your array type Comparable.

class MyImageType: Comparable, Printable {
    var fileID: Int

    // For Printable
    var description: String {
        get {
            return "ID: \(fileID)"
        }
    }

    init(fileID: Int) {
        self.fileID = fileID
    }
}

// For Comparable
func <(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID < right.fileID
}

// For Comparable
func ==(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID == right.fileID
}

let one = MyImageType(fileID: 1)
let two = MyImageType(fileID: 2)
let twoA = MyImageType(fileID: 2)
let three = MyImageType(fileID: 3)

let a1 = [one, three, two]

// return a sorted array
println(sorted(a1)) // "[ID: 1, ID: 2, ID: 3]"

var a2 = [two, one, twoA, three]

// sort the array 'in place'
sort(&a2)
println(a2) // "[ID: 1, ID: 2, ID: 2, ID: 3]"
kwerle
  • 2,225
  • 22
  • 25
7

If you are not using custom objects, but value types instead that implements Comparable protocol (Int, String etc..) you can simply do this:

myArray.sort(>) //sort descending order

An example:

struct MyStruct: Comparable {
    var name = "Untitled"
}

func <(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name < rhs.name
}
// Implementation of == required by Equatable
func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name == rhs.name
}

let value1 = MyStruct()
var value2 = MyStruct()

value2.name = "A New Name"

var anArray:[MyStruct] = []
anArray.append(value1)
anArray.append(value2)

anArray.sort(>) // This will sort the array in descending order
dorian
  • 847
  • 5
  • 11
6

Swift 3,4,5

    struct imageFile  {
        var fileName = String()
        var fileID = Int()
    }
    
    //append objects like this
    var arrImages = [imageFile]()
    arrImages.append(.init(fileName: "Hello1.png", fileID: 1))
    arrImages.append(.init(fileName: "Hello3.png", fileID: 3))
    arrImages.append(.init(fileName: "Hello2.png",fileID: 2))

    
    //array sorting using below code
    let sortImagesArr = arrImages.sorted(by: {$0.fileID < $1.fileID})
    print(sortImagesArr)
    
    //output
    
    imageFile(fileName: "Hello1.png", fileID: 1),
    imageFile(fileName: "Hello2.png", fileID: 2),
    imageFile(fileName: "Hello3.png", fileID: 3)
Priyank Patel
  • 791
  • 8
  • 6
5

I do it like this and it works:

var images = [imageFile]() images.sorted(by: {$0.fileID.compare($1.fileID) == .orderedAscending })

Illya Krit
  • 925
  • 1
  • 8
  • 9
3

If you want to sort original array of custom objects. Here is another way to do so in Swift 2.1

var myCustomerArray = [Customer]()
myCustomerArray.sortInPlace {(customer1:Customer, customer2:Customer) -> Bool in
    customer1.id < customer2.id
}

Where id is an Integer. You can use the same < operator for String properties as well.

You can learn more about its use by looking at an example here: Swift2: Nearby Customers

Hanny
  • 1,322
  • 15
  • 20
3
var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

students.sort(by: >)

print(students)

Prints : "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"

K.Dᴀᴠɪs
  • 9,945
  • 11
  • 33
  • 43
Siddharth Chauhan
  • 1,289
  • 8
  • 15
2

Sort using KeyPath

you can sort by KeyPath like this:

myArray.sorted(by: \.fileName, <) /* using `<` for ascending sorting */

By implementing this little helpful extension.

extension Collection{
    func sorted<Value: Comparable>(
        by keyPath: KeyPath<Element, Value>,
        _ comparator: (_ lhs: Value, _ rhs: Value) -> Bool) -> [Element] {
        sorted { comparator($0[keyPath: keyPath], $1[keyPath: keyPath]) }
    }
}

Hope Swift add this in the near future in the core of the language.

Community
  • 1
  • 1
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • 1
    This has been already answered here https://stackoverflow.com/a/46601105/2303865 along with the mutating method as well. – Leo Dabus May 28 '20 at 03:57
  • the mutating version `public extension MutableCollection where Self: RandomAccessCollection { mutating func sort(_ keyPath: KeyPath, by areInIncreasingOrder: (T, T) throws -> Bool) rethrows where T: Comparable { try sort { try areInIncreasingOrder($0[keyPath: keyPath], $1[keyPath: keyPath]) } }}` – Leo Dabus May 28 '20 at 04:38
1

Swift 3 & 4 & 5

I had some problem related to lowercase and capital case

so I did this code

let sortedImages = images.sorted(by: { $0.fileID.lowercased() < $1.fileID.lowercased() })

and then use sortedImages after that