Sort Array pass Arguments to Func ?

I have multiple times sorting an array so same code over and over, but I'm wanting to narrow it down to use only one function to sort the array.


At the moment using ...

if d { self.array.sort(by: { $0.url_path < $1.url_path }) }
else { self.array.sort(by: { $0.url_path > $1.url_path }) }


Trying something similar to ...

func sortArray(d: Bool, arg1: ?, arg2: ?) {
    if d { self.array.sort(by: { arg1 < arg2 }) }
    else { self.array.sort(by: { arg1 > arg2 }) }
}
sortArray(true, ?, ?)

Accepted Reply

You can write something like the following, utilizing generics and KeyPath.


Assuming the Element type of your `array` is `MyElementType`:

class MyElementType {
    var urlPath: String
    var name: String
    var value: Int
    
    init(urlPath: String, name: String, value: Int) {
        self.urlPath = urlPath
        self.name = name
        self.value = value
    }
}

extension MyElementType: CustomStringConvertible {
    var description: String {
        return "urlPath=\(urlPath),name=\(name),value=\(value)"
    }
}


Then you can write a sorting method as:

class MyClass {
    var array: [MyElementType] = []
    
    func sortArray<SortKeyType: Comparable>(descending: Bool, key: KeyPath<MyElementType, SortKeyType>) {
        if descending {
            self.array.sort(by: { $0[keyPath: key] > $1[keyPath: key] })
        } else {
            self.array.sort(by: { $0[keyPath: key] < $1[keyPath: key] })
        }
    }
    
    //to write an example...
    init(_ elements: (urlPath: String, name: String, value: Int)...) {
        self.array = elements.map(MyElementType.init)
    }
    
    //...
}



Example:

let myObj = MyClass(
    ("/A", "b", 3),
    ("/B", "a", 2),
    ("/C", "c", 1)
)

myObj.sortArray(descending: false, key: \MyElementType.urlPath)
print(myObj.array) //->[urlPath/A,name=b,value=3, urlPath/B,name=a,value=2, urlPath/C,name=c,value=1]

myObj.sortArray(descending: true, key: \MyElementType.value)
print(myObj.array) //->[urlPath=/A,name=b,value=3, urlPath=/B,name=a,value=2, urlPath=/C,name=c,value=1]

Replies

I must be missing something, as I don't understand what you are trying.


In

if d { self.array.sort(by: { $0.url_path < $1.url_path }) }
else { self.array.sort(by: { $0.url_path > $1.url_path }) }

array is an array of a class or struct with url_path property (TheClass)


In the following

func sortArray(d: Bool, arg1: ?, arg2: ?) {
    if d { self.array.sort(by: { arg1 < arg2 }) }
    else { self.array.sort(by: { arg1 > arg2 }) }
}
sortArray(true, ?, ?)

arg1 and arg2 should be of this class TheClass.


then, why don't you call

func sortArray(d: Bool, arg1: TheClass, arg2: TheClass {
    if d { self.array.sort(by: { arg1.url_path < arg2.url_path }) }
    else { self.array.sort(by: { arg1.url_path > arg2.url_path }) }
}
sortArray(true, ?, ?)


Is TheClass Comparable ? That would probably be the best to make it comparable

func sortArray(d: Bool, arg1: TheClass, arg2: TheClass {
    if d { self.array.sort(by:< ) }
    else { self.array.sort(by: > ) }
}

Thanks, you make it seem so easy. Yeh, the problem was figuring "TheClass"'s property. Sorry was a bit unclear "url_path" could be something else. (it's a struct)

So, does it work now ?


If so, wish you good continuation (and don't forget to close the thread).

Sorry no ... I should change "the problem was figuring out" to "the problem is figuring out". Still no idea how to pass the property as an argument.

Could you show the struct declaration ?

You can write something like the following, utilizing generics and KeyPath.


Assuming the Element type of your `array` is `MyElementType`:

class MyElementType {
    var urlPath: String
    var name: String
    var value: Int
    
    init(urlPath: String, name: String, value: Int) {
        self.urlPath = urlPath
        self.name = name
        self.value = value
    }
}

extension MyElementType: CustomStringConvertible {
    var description: String {
        return "urlPath=\(urlPath),name=\(name),value=\(value)"
    }
}


Then you can write a sorting method as:

class MyClass {
    var array: [MyElementType] = []
    
    func sortArray<SortKeyType: Comparable>(descending: Bool, key: KeyPath<MyElementType, SortKeyType>) {
        if descending {
            self.array.sort(by: { $0[keyPath: key] > $1[keyPath: key] })
        } else {
            self.array.sort(by: { $0[keyPath: key] < $1[keyPath: key] })
        }
    }
    
    //to write an example...
    init(_ elements: (urlPath: String, name: String, value: Int)...) {
        self.array = elements.map(MyElementType.init)
    }
    
    //...
}



Example:

let myObj = MyClass(
    ("/A", "b", 3),
    ("/B", "a", 2),
    ("/C", "c", 1)
)

myObj.sortArray(descending: false, key: \MyElementType.urlPath)
print(myObj.array) //->[urlPath/A,name=b,value=3, urlPath/B,name=a,value=2, urlPath/C,name=c,value=1]

myObj.sortArray(descending: true, key: \MyElementType.value)
print(myObj.array) //->[urlPath=/A,name=b,value=3, urlPath=/B,name=a,value=2, urlPath=/C,name=c,value=1]
public struct myStruct: Codable {
    var url_path: String
    var bytes: Double

    init(_ path: String,_ bytes: Double) {
        self.url_path = path
        self.bytes = bytes
    }

I would make the class Comparable


extension myStruct: Comparable {

  public static func == (lhs: myStruct, rhs: myStruct) -> Bool {
    return lhs.url_path == rhs.url_path &&
    lhs.bytes == rhs.bytes
  }


  public static func < (lhs: myStruct, rhs: myStruct) -> Bool {
    return lhs.url_path < rhs.url_path
     }

  public static func > (lhs: myStruct, rhs: myStruct) -> Bool {
    return lhs.url_path > rhs.url_path
     }

}


Then


func sortArray(d: Bool, array: inout [myStruct]) {
    if d { array.sort(by:< ) }
    else { array.sort(by: > ) }
}

var myArray :[myStruct] = [myStruct("hello", 5), myStruct("beginning", 9), myStruct("before", 6)]
print(myArray)

sortArray(d: true, array: &myArray)
print(myArray)

sortArray(d: false, array: &myArray)
print(myArray)


Which yields to:


[__lldb_expr_3.myStruct(url_path: "hello", bytes: 5.0), __lldb_expr_3.myStruct(url_path: "beginning", bytes: 9.0), __lldb_expr_3.myStruct(url_path: "before", bytes: 6.0)]

[__lldb_expr_3.myStruct(url_path: "before", bytes: 6.0), __lldb_expr_3.myStruct(url_path: "beginning", bytes: 9.0), __lldb_expr_3.myStruct(url_path: "hello", bytes: 5.0)]

[__lldb_expr_3.myStruct(url_path: "hello", bytes: 5.0), __lldb_expr_3.myStruct(url_path: "beginning", bytes: 9.0), __lldb_expr_3.myStruct(url_path: "before", bytes: 6.0)]

KeyPath works also on structs.


public struct MyStruct: Codable {
    var urlPath: String
    var bytes: Double
    
    init(_ path: String,_ bytes: Double) {
        self.urlPath = path
        self.bytes = bytes
    }
    
    private enum CodingKeys: String, CodingKey {
        case urlPath = "url_path"
        case bytes = "bytes"
    }
}

extension MyStruct: CustomStringConvertible {
    public var description: String {
        return "urlPath=\(urlPath),bytes=\(bytes)"
    }
}



class MyClass {
    var array: [MyStruct] = []
    
    func sortArray<SortKeyType: Comparable>(descending: Bool, key: KeyPath<MyStruct, SortKeyType>) {
        if descending {
            self.array.sort(by: { $0[keyPath: key] > $1[keyPath: key] })
        } else {
            self.array.sort(by: { $0[keyPath: key] < $1[keyPath: key] })
        }
    }
    
    ////to write an example...
    init(_ elements: (path: String, bytes: Double)...) {
        self.array = elements.map(MyStruct.init)
    }
    
    //...
}


Example:

let myObj = MyClass(
    ("/A", 3),
    ("/B", 1),
    ("/C", 2)
)

myObj.sortArray(descending: false, key: \.urlPath)
print(myObj.array) //->[urlPath=/A,bytes=3.0, urlPath=/B,bytes=1.0, urlPath=/C,bytes=2.0]

myObj.sortArray(descending: true, key: \.bytes)
print(myObj.array) //->[urlPath=/A,bytes=3.0, urlPath=/C,bytes=2.0, urlPath=/B,bytes=1.0]


You can write a simple extension for Array, if you prefer.

extension Array {
    mutating func sort<SortKeyType: Comparable>(descending: Bool = false, key: KeyPath<Element, SortKeyType>) {
        if descending {
            self.sort {$0[keyPath: key] > $1[keyPath: key]}
        } else {
            self.sort {$0[keyPath: key] < $1[keyPath: key]}
        }
    }
}



extension Array {
    mutating func sort<SortKeyType: Comparable>(descending: Bool = false, key: KeyPath<Element, SortKeyType>) {
        if descending {
            self.sort {$0[keyPath: key] > $1[keyPath: key]}
        } else {
            self.sort {$0[keyPath: key] < $1[keyPath: key]}
        }
    }
}

var myArray = [
    MyStruct("/A", 3),
    MyStruct ("/B", 1),
    MyStruct("/C", 2)
]
myArray.sort(key: \.bytes)
print(myArray) //->[urlPath=/B,bytes=1.0, urlPath=/C,bytes=2.0, urlPath=/A,bytes=3.0]