5

I have a Set of objects, let's say Fruits:

let uniqueFruits = Set(Fruit("Apple"), Fruit("Banana"), Fruit("Orange"))

And want to sort them based on a certain atteribute. In this case "size".

I cannot find a way to do this based on the documentation from Apple: https://developer.apple.com/reference/foundation/nsmutableset

How can I sort a Set by a certain attribute?

  • 3
    From the documentation: "The NSMutable​Set class declares the programmatic interface to a mutable, **unordered** collection of distinct objects." – Nirav D Apr 10 '17 at 12:57
  • 2
    Also `Set` is different from `NSMutableSet`. – Martin R Apr 10 '17 at 13:04
  • You'll find `sorted()` and `sorted(by:)` on the [`Set` API reference](https://developer.apple.com/reference/swift/set) page, with an example. – Martin R Apr 10 '17 at 13:09

2 Answers2

6

You have to convert the Set to an Array.

The reason for this is the following definition:

"Sets are different in the sense that order does not matter and these will be used in cases where order does not matter."

Whereas a set:

"... stores distinct values of the same type in a collection with no defined ordering."

See for further information: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html


In this case you will have a list of distinct values (and I consider your decision to use a NSSet as valid argument) you will have to transform your set to an array, you should not run into trouble, as your set already seems to take care of that your objects are of the same type (e.g. "Fruit").

So in this case, we will have

  1. Define Sort Criteria
  2. Sort the Array

I have attached a sample for both Objective-C and Swift, in case you need the one way or the other:

Objective-C Code

NSMutableSet<Fruit> *uniqueFruits = [NSMutableSet new];
[uniqueFruits addObject:[[Fruit alloc] initWithName:@"Apple"]];
[uniqueFruits addObject:[[Fruit alloc] initWithName:@"Banana"]];
[uniqueFruits addObject:[[Fruit alloc] initWithName:@"Orange"]];

// 1 Define Sort Criteria
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"size" ascending:YES]; // Key is the NSString of a certain selector. If it is an attribute of another class reference. Simply use "reference.property".

// 2 Sort the Array
NSArray<Fruit> *sortedArray = [self.uniqueFruits sortedArrayUsingDescriptors:@[descriptor]];

Swift 3 Code

let uniqueFruits = Set<Fruit>(Fruit("Apple"), Fruit("Banana"), Fruit("Orange"))

// 1 & 2 Define Sort Criteria and sort the array, using a trailing closure that sorts on a field/particular property you specify
// Be aware: result is an array
let sortedArray = uniqueFruits.sort({ $0.size < $1.size })
Lepidopteron
  • 6,056
  • 5
  • 41
  • 53
  • 1
    You can call `sorted(where:)` directly on a set, resulting in an array. – Martin R Apr 10 '17 at 13:02
  • `Set(new Fruit("Apple"), new Fruit("Banana"), new Fruit("Orange"))` will not compile – There's no `new` keyword in Swift and `Set` doesn't have an `init(_:)` that takes a variadic list of elements. – Hamish Apr 10 '17 at 13:23
  • 1
    Your Obj-C code also won't compile – you're missing a bunch of `*`s and some closing square-brackets. – Hamish Apr 10 '17 at 13:27
  • Thank you. It is a bit hard to switch contexts and code in an online text editor without compiler. Even more if people would expect copy paste solutions ;) Still no reason for a downvote. – Lepidopteron Apr 10 '17 at 13:31
  • Thank you, just needed the right direction, not the final code. This gives me a great Input to continue. –  Apr 10 '17 at 13:33
  • 1
    `NSSet` also has an Objective-C `sortedArrayUsingDescriptors` method. – Martin R Apr 10 '17 at 13:35
  • Sets **can** be sorted. For example in C++ `std::set` is a *sorted associative container*, which is very useful in some algorithms. Unfortunately neither Swift, nor Objective-C provide this type as a part of standard libraries. – Sergiy Salyuk Aug 31 '19 at 11:15
4

There is NSOrderedSet and its mutable sibling NSMutableOrderedSet which are exactly that: a set that maintains an order. The mutable ordered also has various methods to sort the set. In Swift, it's a bit awkward to use since you can't create a NSMutableOrderedSet<Fruit> and can only use it with objects anyway.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • Do you know their internal implementation? (binary search tree? hash table?) – Ricardo Apr 26 '20 at 10:31
  • 1
    No, and an important point of all Cocoa collections is that their algorithm is not exposed (like, for example, in Java) and can differ by OS version, depending on number of elements and other factors. The ordered set is a [class cluster](https://stackoverflow.com/questions/1844158/what-exactly-is-a-so-called-class-cluster-in-objective-c), like most Cocoa collections. – DarkDust Apr 26 '20 at 14:49