shallowThought
shallowThought

Reputation: 19602

How to sort an Array of Tuples using sorted? (Cannot invoke 'sorted' with an argument list of type)

I am trying to sort an array of tuples using sorted.

The error I get is:

Cannot invoke 'sorted' with an argument list of type (((MyClass1, MyClass2)) -> Bool)

The array is created like this:

class MyClass1 {
    var id: Int
    init(id:Int) { self.id = id }
}

class MyClass2 {
    var id2: Int
    init(id2:Int) { self.id2 = id2 }
}
let array1 = [MyClass1(id:4), MyClass1(id:3), MyClass1(id:2), MyClass1(id:1)]
let array2 = [MyClass2(id2:1), MyClass2(id2:2), MyClass2(id2:3), MyClass2(id2:4)]
var tuples = zip(array1, array2)

What I have tried:

tuples = tuples.sorted { (left, right) -> Bool in
    return left.1.id2 > right.1.id2
}

tuples = tuples.sorted { $0.1.id2 > $1.1.id2 }

tuples = tuples.sorted(by: { (first: (MyClass1, MyClass2), second: (MyClass1, MyClass2)) -> Bool in
    return first.1.id2 > second.1.id2
})

The expected output is(pseudo, handwritten):

[(myClass1WithId_1, myClass2WithId2_4),(myClass1WithId_2, myClass2WithId2_3),(myClass1WithId_3, myClass2WithId2_2),(myClass1WithId_4, myClass2WithId2_1)]

The tuples sorted by myClass2.id2 descending.


Update

I found out how to workaround the issue. The error seems to be wrong/misleading.

I can sort it when assigning the result to a new variable:

let tuples2 = tuples.sorted { (left, right) -> Bool in
    return left.1.id2 > right.1.id2
}

while assigning it to the existing variable gives me the mentioned error:

tuples = tuples.sorted { (left, right) -> Bool in
    return left.1.id2 > right.1.id2
}

Upvotes: 2

Views: 260

Answers (2)

matt
matt

Reputation: 535087

I am trying to sort an array of tuples

But problem is that your tuples is not an array. (It is a thing called a Zip2Sequence.) The solution is to coerce it to an array:

var tuples = Array(zip(array1, array2))

Now your original code will work:

let array1 = [MyClass1(id:4), MyClass1(id:3), MyClass1(id:2), MyClass1(id:1)]
let array2 = [MyClass2(id2:1), MyClass2(id2:2), MyClass2(id2:3), MyClass2(id2:4)]
var tuples = Array(zip(array1, array2)) // *
tuples = tuples.sorted { (left, right) -> Bool in
    return left.1.id2 > right.1.id2
}

The reason your solution worked is that a sorted sequence yields an array. So you can't reassign the array to the sequence variable (tuples) but you can assign it to a different variable (tuples2). My solution starts with an array, so the resulting sorted array can be assigned back to the same variable.

Upvotes: 1

Alexander
Alexander

Reputation: 63197

You're not able to use sorted() because the tuple type (MyClass1, MyClass2) does not conform to Comparable, so the sorter would not know how to sort your tuples. You can't add an extension that adds conformance to it either, because tuples can't have extensions or conform to protocols.

You can however, use sorted(by:), which requires you give a closure which defines the sorting between elements.

class MyClass1 {
    var id: Int
    init(id:Int) { self.id = id }
}

class MyClass2 {
    var id2: Int
    init(id2:Int) { self.id2 = id2 }
}
let array1 = [MyClass1(id:4), MyClass1(id:3), MyClass1(id:2), MyClass1(id:1)]
let array2 = [MyClass2(id2:1), MyClass2(id2:2), MyClass2(id2:3), MyClass2(id2:4)]
let tuples = zip(array1, array2)

let sortedTuples = tuples.sorted{ $0.1.id2 > $1.1.id2 }

print(sortedTuples)

Upvotes: 0

Related Questions