How to compare elements of two arrays - arrays

I want to compare the elements of two arrays and check if they are equal.
I already tried various solutions but nothing really works.
I tried the solution from
How to compare two array of objects?
This is my object:
struct AccountBalance: Decodable {
let balance: Double
let currency: String
init(balance: Double, currency: String ) {
self.currency = currency
self.balance = balance
}
enum CodingKeys: String, CodingKey {
case currency = "Currency"
case balance = "Balance"
}
}
This is the code from the link I tried:
let result = zip(accountBalance, getsSaved).enumerate().filter() {
$1.0 == $1.1
}.map{$0.0}
But I get this error:
Closure tuple parameter '(offset: Int, element: (AccountBalance, AccountBalance))' does not support destructuring with implicit parameters

Array provides a function elementsEqual which is able to compare two arrays without explicitly conforming to Equatable:
let result = accountBalance.elementsEqual(getsSaved) {
$0.balance == $1.balance && $0.currency == $1.currency
}
Edit:
If you want to have the equality result regardless of the order of objects in the array then you can just add sort with each of the arrays.
let result = accountBalance.sorted { $0.balance < $1.balance }.elementsEqual(getsSaved.sorted { $0.balance < $1.balance }) {
$0.balance == $1.balance && $0.currency == $1.currency
}

I am guessing two arrays should be considered equal when they contain the same elements, regardless of ordering.
First, implement Equatable and Hashable.
I am using hashValue as an id so I can sort the arrays first.
Here is what your AccountBalance class should look like:
struct AccountBalance: Decodable, Equatable, Hashable {
// Important parts!
var hashValue: Int{
return balance.hashValue ^ currency.hashValue &* 1677619
}
static func == (lhs: AccountBalance, rhs: AccountBalance) -> Bool{
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
}
Then create an algorithm that sorts the ararys and then check each elements by one by by if the contents are the same.
Here is the function that take use of Equatable and Hashable.
func isEqual(arr1: [AccountBalance], arr2: [AccountBalance]) -> Bool{
if arr1.count != arr1.count{
return false
}
let a = arr1.sorted(){
$0.hashValue > $1.hashValue
}
let b = arr2.sorted(){
$0.hashValue > $1.hashValue
}
let result = zip(a, b).enumerated().filter() {
$1.0 == $1.1
}.count
if result == a.count{
return true
}
return false
}

I will suggest you implement the accepted answer of the link you provided because it controls that both arrays are the same size and it orders them.
But if you want that your code works I solved like this:
In order to have control of the comparison your struct should implement the Equatable protocol and overload the operator ==
extension AccountBalance : Equatable {}
func ==(lhs: AccountBalance, rhs: AccountBalance) -> Bool {
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
Then compare both arrays and check if contains false, if it does, one or more items in the array aren't the same.
let result = !zip(accountBalance, getsSaved).enumerated().map() {
$1.0 == $1.1
}.contains(false)
Hope it helps you

I'm not sure about what the rest of your code does, but making the parameters explicit does make XCode happy:
let result = zip(accountBalance, getsSaved).enumerated().filter() { (arg) -> Bool in
let (_, (balance1, balance2)) = arg
return balance1.balance == balance2.balance
}.map{ $0.0 }`
Tried (_, (balance1, balance2)) -> Bool in directly, but it wouldn't let me either

Sample with several arrays:
import Foundation
let arr1: [String?] = ["word", nil, "word3", "word4"]
let arr2: [Double?] = [1.01, 1.02, nil, 1.04]
let arr3: [Int?] = [nil, 2, 3, 4]
var tuple1: [(title: String, number: String, coord: String)] = []
let countArray = arr1.count
for element in 0..<countArray {
tuple1.append((arr1[element].map
{String($0)} ?? "", arr2[element].map
{String($0)} ?? "", arr3[element].map
{String($0)} ?? ""))
}
print(tuple1)
result of printing:
[(title: "word1", number: "1.01", coord: ""), (title: "", number: "1.02", coord: "2"), (title: "word3", number: "", coord: "3"), (title: "word4", number: "1.04", coord: "4")]

Related

Sorting Array in Swift3

In my code, I have a struct like the following:
struct Object {
var name: String
var count: Int
I am now creating an array of 10 Objects with random names and random counts.
Is there an easy way to
a) sort them alphabetically
b) sort them numerically in ascending order
Basically, there will be an array like so:
[Object1, Object2, Object3].
Every Object has a name and count attribute, and I want the objects in that list be sorted via these two attributes.
Solution in Swift2 (using this solution: StackOverflow):
Object.sort{
if $0.name != $1.name {
return $0.name < $1.name
}
else {
//suits are the same
return $0.count < $1.count
}
}
However, this has been renamed to sorted(by: ) in Swift3, and I don't quit get how to do that.
If you want to sort alphabetically and then numerically, you can:
var array = ["A2", "B7", "A4", "C3", "A1", "A10"]
array.sort { $0.compare($1, options: .numeric) == .orderedAscending }
That produces:
["A1", "A2", "A4", "A10", "B7", "C3"]
I added A10 to your array, because without it, a simple alphabetic sort would have been sufficient. But I'm assuming you wanted A10 after A4, in which case the numeric comparison will do the job for you.
You changed the example to be a struct with two properties. In that case, you can do something like:
struct Foo {
var name: String
var count: Int
}
var array = [
Foo(name:"A", count: 2),
Foo(name:"B", count: 7),
Foo(name:"A", count: 7),
Foo(name:"C", count: 3),
Foo(name:"A", count: 1),
Foo(name:"A", count: 10)
]
array.sort { (object1, object2) -> Bool in
if object1.name == object2.name {
return object1.count < object2.count
} else {
return object1.name < object2.name
}
}
Or, more concisely:
array.sort { $0.name == $1.name ? $0.count < $1.count : $0.name < $1.name }
Or
array.sort { ($0.name, $0.count) < ($1.name, $1.count) }
Note, rather than putting this logic in the closure, I'd actually make Foo conform to Comparable:
struct Foo {
var name: String
var count: Int
}
extension Foo: Equatable {
static func ==(lhs: Foo, rhs: Foo) -> Bool {
return (lhs.name, lhs.count) == (rhs.name, rhs.count)
}
}
extension Foo: Comparable {
static func <(lhs: Foo, rhs: Foo) -> Bool {
return (lhs.name, lhs.count) < (rhs.name, rhs.count)
}
}
This keeps the comparison logic nicely encapsulated within the Foo type, where it belongs.
Then you can just do the following to sort in place:
var array = ...
array.sort()
Or, alternatively, you can return a new array if you don't want to sort the original one in place:
let array = ...
let sortedArray = array.sorted()
Narusan, maybe this will help you.
Let's say you have an array with your struct objects called objArray, then you can order it by the code bellow:
var objArray = [Object]()
objArray.append(Object(name:"Steve", count:0))
objArray.append(Object(name:"Alex", count:1))
objNameSorted = objArray.sorted (by: {$0.name < $1.name})
objNCountSorted = objArray.sorted (by: {$0.count < $1.count})
You can still use shorthand for sorted:
objNameSorted = objArray.sorted { $0 < $1 }
While less readable, it more closely mimics the sort syntax.

Sorting the [Any] array

Given an array defined as follow
let list: [Any]
I want to sort it WHEN
all the values inside it have the same type Element
AND Element is Comparable.
When it should return the sorted array
So I would need a function that when the array is populated in a way like the followings
let list: [Any] = [10, 11, 0, 2, -1]
let list: [Any] = ["Red", "Green", "Blue"]
let list: [Any] = [true, false, true, true]
does return the sorted array.
When it should return nil
On the other hand when list contains one of the following examples
let list: [Any] = [CGPointZero, CGPoint(x:1, y:1)] // CGPoint is not comparable
let list: [Any] = [10, "Hello"] // Values of different types
I want nil as return value.
Any idea?
Compile time solution
extension _ArrayType where Generator.Element == Any {
func sortQ() -> Any? {
return nil
}
}
extension _ArrayType where Generator.Element: Comparable {
func sortQ() -> [Self.Generator.Element] {
return self.sort(<)
}
}
// Because Bool is not comparable by default...
extension Bool: Comparable {
}
public func < (lhs: Bool, rhs: Bool) -> Bool {
return !lhs && rhs // or Int(lhs) < Int(rhs)
}
[10, 11, 0, 2, -1].sortQ() //[-1, 0, 2, 10, 11]
["Red", "Green", "Blue"].sortQ() //["Blue", "Green", "Red"]
[true, false, true, true].sortQ() //[false, true, true, true]
[CGPointZero, CGPoint(x:1, y:1)].sortQ() //nil
[10, "Hello"].sortQ() //nil
Runtime solutions:
UPDATE
Here is non final state. The problem is with casting to comparable. IMHO it is not possible. Until now I didn't know about trick with optional type. Anyway even casting of meta type is not possible because type is not known until runtime. My weak workaround is to list supported comparable types:
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
let f = Mirror(reflecting: firstElement)
let s = Mirror(reflecting: secondElement)
guard f.subjectType == s.subjectType else {
arrayOK = false
return false
}
switch String(f.subjectType) {
case "Int":
return (firstElement as! Int) < (secondElement as! Int)
case "String":
return (firstElement as! String) < (secondElement as! String)
case "Bool":
return (firstElement as! Bool) < (secondElement as! Bool)
default:
arrayOK = false
return false
}
}
return arrayOK ? sortedArray : nil
}
}
UPDATE 2
The second option is to have comparable protocol defined differently (AnyComparable). Unfortunately it means to create extensions for all Comparable types.
Otherwise there's no way, at compile-time, the compiler can find the correct function/operator (as it doesn't know the types ahead of time).
So you have two options:
if you had some idea of the types you were comparing and define
them explicitly (update 1).
Use interface which does not use Self
type (update 2).
IMHO there is no other solution
protocol AnyComparable {
func compareTo(second: Any) -> Bool
}
extension AnyComparable where Self: Comparable {
func compareTo(second: Any) -> Bool {
if let secondSameType = second as? Self {
return self < secondSameType
}
return false
}
}
extension Int: AnyComparable {
}
extension String: AnyComparable {
}
extension Bool: AnyComparable {
}
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
var wantedType: Any.Type?
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
if wantedType == nil {
wantedType = Mirror(reflecting: firstElement).subjectType
}
guard let f = firstElement as? AnyComparable where wantedType == Mirror(reflecting: secondElement).subjectType else {
arrayOK = false
return false
}
return f.compareTo(secondElement)
}
return arrayOK ? sortedArray : nil
}
}
For the moment, I wrote a little extension to check if all the elements are of the same type (I will be working on this to check if can get a result):
extension _ArrayType where Generator.Element == Any{
func hasEqualTypeAndComparable()->Bool{
if self.count > 0{
let firstType = self.first?.dynamicType
for val in self{
if firstType != val.dynamicType{
return false
}
}
return self.first is Comparable
}
return false
}
}
Example:
//Example 1
var values:[Any] = [2,1,4,3,"Hola"]
values.hasEqualTypeAndComparable() // Print false
//Example 2
var values:[Any] = [2,1,4,3]
values.hasEqualTypeAndComparable() // Prints true
If your use case allows you to provide a hint to the compiler, you could specify a filter on the type of output that you want:
extension _ArrayType where Generator.Element == Any {
func filterByType<T: Comparable>() -> [T] {
var output = [T]()
for i in self {
if let j = i as? T {
output.append(j)
}
}
return output
}
}
If the input array does not contain any elements of the specified type then it will just return an empty array. If the type is not a Comparable, then the code won't event compile.
Example:
let list: [Any] = [10, "Hello", 3, false, "Foo", "Bar", 1] // Values of different types
var output = list.filterByType() as [Int]
output.sortInPlace()

How to compare Equatable in Swift

I have multiple sets of two arrays like that. I am getting them from a 3rd party.
var array1 : [Any?]
var array2 : [Any?]
I know about types of objects in these array (in compile time). As example that first element will be a String and second is an Int.
I currently compare each set of arrays like that (please notice that arrays aren't homogeneous).
array1[0] as? String == array2[0] as? String
array1[1] as? Int == array2[1] as? Int
...
The biggest problem that each set have different types in it. As result, I have let say 10 sets of arrays with 5 elements in each. I had to do 10*5 explicit conversion to specific type and comparation.
I want to be able to write a common method which can compare two arrays like that (without a need to specify all the types)
compareFunc(array1, array2)
I started to go down the road. However, I can't figure out what should I cast the objects too. I tried Equatable. However, swift doesn't like direct usage of it. So, something like that doesn't work
func compare(array1: [Any?], array2: [Any?]) {
for index in 0..<array1.count {
if (array1[index] as? Equatable != array2[index] as? Equatable) {
// Do something
}
}
}
Each object in this array will be Equatable. However, I am not sure how to use it in this case.
Construct a simple element-by-element comparison function based on (attempted) type conversion to known element types
Since you're aiming to compare arrays of (optional) Any elements, you could just construct a function that performs element-by-element comparison by using a switch block to attempt to downcast the elements of the array to different known types in your "3rd party arrays". Note that you needn't specify which element position that corresponds to a specific type (as this might differ between different set of arrays), just the exhaustive set of the different types that any given element might be of.
An example of such a function follows below:
func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {
/* element-by-element downcasting (to known types) followed by comparison */
return arr1.count == arr2.count && !zip(arr1, arr2).contains {
/* note that a 'true' below indicates the arrays differ (i.e., 'false' w.r.t. array equality) */
if let v1 = $1 {
/* type check for known types */
switch $0 {
case .None: return true
case let v0 as String:
if let v1 = v1 as? String { return !(v0 == v1) }; return true
case let v0 as Int:
if let v1 = v1 as? Int { return !(v0 == v1) }; return true
/* ...
expand with the known possible types of your array elements
... */
case _ : return true
/* */
}
}
else if let _ = $0 { return true }
return false
}
}
or, alternative, making the switch block a little less bloated by making use of (a slightly modified of) the compare(...) helper function from #Roman Sausarnes:s answer
func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {
/* modified helper function from #Roman Sausarnes:s answer */
func compare<T: Equatable>(obj1: T, _ obj2: Any) -> Bool {
return obj1 == obj2 as? T
}
/* element-by-element downcasting (to known types) followed by comparison */
return arr1.count == arr2.count && !zip(arr1, arr2).contains {
/* note also that a 'true' below indicates the arrays differ
(=> false w.r.t. equality) */
if let v1 = $1 {
/* type check for known types */
switch $0 {
case .None: return true
case let v0 as String: return !compare(v0, v1)
case let v0 as Int: return !compare(v0, v1)
/* ...
expand with the known possible types of your array elements
... */
case _ : return true
/* */
}
}
else if let _ = $0 { return true }
return false
}
}
Example usage:
/* Example usage #1 */
let ex1_arr1 : [Any?] = ["foo", nil, 3, "bar"]
let ex1_arr2 : [Any?] = ["foo", nil, 3, "bar"]
compareAnyArrays(ex1_arr1, ex1_arr2) // true
/* Example usage #2 */
let ex2_arr1 : [Any?] = ["foo", nil, 2, "bar"]
let ex2_arr2 : [Any?] = ["foo", 3, 2, "bar"]
compareAnyArrays(ex2_arr1, ex2_arr2) // false
This is a marginal solution, but it should reduce some of the duplicative code that you are trying to avoid:
func compareAnyArray(a1: [Any?], _ a2: [Any?]) -> Bool {
// A helper function for casting and comparing.
func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
return obj1 as? T == obj2 as? T
}
guard a1.count == a2.count else { return false }
return a1.indices.reduce(true) {
guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }
switch $1 {
// Add a case statement for each index in the array:
case 0:
return $0 && compare(_a1, _a2, t: Int.self)
case 1:
return $0 && compare(_a1, _a2, t: String.self)
default:
return false
}
}
}
That isn't the most beautiful solution, but it will reduce the amount of code you have to write, and it should work for any two [Any?], as long as you know that the type at index 0 is an Int, and the type at index 1 is a String, etc...
A compact and Swift 5 version of great solution of Aaron Rasmussen:
func compare(a1: [Any?], a2: [Any?]) -> Bool {
guard a1.count == a2.count else { return false }
func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
return obj1 as? T == obj2 as? T
}
return a1.indices.reduce(true) {
guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }
switch $1 {
case 0: return $0 && compare(obj1: _a1, _a2, t: Int.self)
case 1: return $0 && compare(obj1: _a1, _a2, t: String.self)
case 2: return $0 && compare(obj1: _a1, _a2, t: <#TypeOfObjectAtIndex2#>.self)
default: return false
}
}
}

How to get list of common elements of 2 array in Swift?

I have two arrays:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
How can I get the list of common items in those two array which gives
ouptput = ["mango", "blueberry"]
I can't use if contains(array, string) as I want to compare 2 arrays.
You can also use filter and contains in conjunction:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
// only Swift 1
let output = fruitsArray.filter{ contains(vegArray, $0) }
// in Swift 2 and above
let output = fruitsArray.filter{ vegArray.contains($0) }
// or
let output = fruitsArray.filter(vegArray.contains)
Set vs Array for a single computation of common elements
We consider the following code snippet:
let array1: Array = ...
let array2: Array = ...
// `Array`
let commonElements = array1.filter(array2.contains)
// vs `Set`
let commonElements = Array(Set(array1).intersection(Set(array2)))
// or (performance wise equivalent)
let commonElements: Array = Set(array1).filter(Set(array2).contains)
I have made some (artificial) benchmarks with Int and short/long Strings (10 to 100 Characters) (all randomly generated). I always use array1.count == array2.count
I get the following results:
If you have more than critical #(number of) elements converting to a Set is preferable
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explanation of the results
Using the Array approach uses "Brute force"-search which has time complexity O(N^2) where N = array1.count = array2.count which is in contrast to the Set approach O(N). However the conversion from Array to Set and back is very expensive for large data which explains the increase of critical #elements for bigger data types.
Conclusion
For small Arrays with about 100 elements the Array approach is fine but for larger ones you should use the Set approach.
If you want to use this "common elements"-operation multiple times it is advisable to use Sets only if possible (the type of the elements has to be Hashable).
Final Remarks
A conversion from Array to Set is kind of expensive while the conversion from Set to Array is in contrast very inexpensive.
Using filter with .filter(array1.contains) is performance wise faster than .filter{ array1.contains($0) } since:
the last one creates a new closure (only once) whereas the first one passes only a function pointer
for the last one the call of the closure creates an additional stack frame which costs space and time (multiple times: O(N))
Convert them to Set and use intersect() function:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let fruitsSet = Set(fruitsArray)
let vegSet = Set(vegArray)
let output = Array(fruitsSet.intersection(vegSet))
You don't need a Set (as the comments above have mentioned).
You could instead use a generic function, similar to the one Apple use in their Swift Tour, and thus avoid casting:
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
This function can take any two arrays (SequenceTypes) and if any of their elements are the same it returns true.
You could simply modify this generic function to package up an array of strings and return that instead.
For example like this:
func arrayOfCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
returnArray.append(lhsItem)
}
}
}
return returnArray
}
Usage like this:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
The added benefit here is that this function also works with all same typed arrays. So later if you need to compare two [myCustomObject] arrays, once they both conform to equatable, you're all set! (pun intended)
Edit: (For non common elements) you could do something like this
func arrayOfNonCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
var found = false
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
found = true
break
}
}
if (!found){
returnArray.append(lhsItem)
}
found = false
}
for rhsItem in rhs {
for lhsItem in lhs {
if rhsItem == lhsItem {
found = true
break
}
}
if (!found){
returnArray.append(rhsItem)
}
found = false
}
return returnArray
}
This implementation is ugly though.
A generic method, inspired by The Swift Programming Language (Swift 3) exercise:
func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var common: [T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
common.append(lhsItem)
}
}
}
return common
}
Then, use it like this:
var a = [3,88,74]
var b = [1,3,88]
print("commons: \(commonElements(a, b))")
--> commons: [3, 88]
The following works with swift 4:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
var someHash: [String: Bool] = [:]
fruitsArray.forEach { someHash[$0] = true }
var commonItems = [String]()
vegArray.forEach { veg in
if someHash[veg] ?? false {
commonItems.append(veg)
}
}
print(commonItems)
The following works with swift 4,5
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
//You can using filter function
let commonArray = fruitsArray.filter { fruit in
vegArray.contains(fruit)
}
print(commonArray)
Output: ["mango", "blueberry"]
Using Set and also intersection as follows:
func findIntersection (firstArray : [Int], secondArray : [Int]) -> [Int]
{
return [Int](Set<Int>(firstArray).intersection(secondArray))
}
print (findIntersection(firstArray: [2,3,4,5], secondArray: [1,2,3]))

How to sort an array of custom objects by property value in Swift

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?
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.
[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)
Swift 3
people = people.sorted(by: { $0.email > $1.email })
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.
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.
In Swift 3.0
images.sort(by: { (first: imageFile, second: imageFile) -> Bool in
first. fileID < second. fileID
})
You can also do something like
images = sorted(images) {$0.fileID > $1.fileID}
so your images array will be stored as sorted
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
}
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.
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)
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
}
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]"
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
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.
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)
I do it like this and it works:
var images = [imageFile]()
images.sorted(by: {$0.fileID.compare($1.fileID) == .orderedAscending })
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
var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
students.sort(by: >)
print(students)
Prints : "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"
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.
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

Resources