Sorting an array breakdown - objective-c

I've been reading through a few different programs that use the sortUsingSelector method to sort an array with string objects and I can't figure out how they do it.
Each program begins by defining the sort method as follows:
[myBook sort]; /**myBook is the name of the array in the addressBook class**/
-(void) sort
{
[book sortUsingSelector: #selector (compareNames:)];
}
/**compareNames is defined in the addressCard class**/
The sort method uses a selector method that seems to do all the work:
-(NSComparisonResult) compareNames: (AddressCard *) element
{
return [name compare: element.name];
}
Its important to note that there are two different classes: addressCard and addressBook.
I know the compare method returns NSOrderedAscending, NSOrderedSame, or NSOrderedDescending to the sortUsingSelector method. But, how do these methods go about sorting everything? I feel like I'm missing something huge. For instance, how does the compare method know which elements in the array to compare? I imagine that element[0] of the array is compared with element[1] and then a sort occurs then the next element is compared...Does the compare method have a default definition that I'm overlooking?

The compare: method (or any method you use as the parameter in sortUsingSelector:) just has one job: given exactly two objects (a pair), tell me how to order them. That is all it does.
It knows how to do this because it is defined by the class it is sent to. In this case, name is an NSString so we use NSString's definition of the compare: method - a definition that knows how to order strings (using rules about alphabetical order).
It is the sortUsingSelector: method that actually hands the compare: method pairs to, uh, compare. And how it chooses those pairs is its business. You are not told how it picks those pairs. Deciding what pairs to choose and in what order is a deep business; it is the subject of your Computer Science 101 class. But you are shielded deliberately from those details in this situation.

Related

Return a key : value pair from a method for use in NSDictionary

I understand I can return an NSDictionary by doing
- (NSDictionary *)keyWithValue {
return #{#"key" : #"value"};
}
but how can I return that without the enclosing #{} dictionary?
There is no tuples in Objective C unlike in Swift, Python etc. So the common way to return 2 different objects is to return an array or a dictionary.
You also can try something like:
- (NSString *)keyWithValue:(NSString**)value {
*value = #"value";
return #"key";
}
It should be used following way:
NSString *v;
NSString *k = [self keyWithValue:&v];
// now v contains #"value"
Objective-C, like C before it, doesn't allow the return of multiple values from a method. (Essentially, although a method or function can accept any number of arguments as input, it can only have a single return value.) There are historical and implementation reasons for this design but it can be frustrating when you simply have a pair/tuple to return.
If you have a method that has two distinct "results" that you need to return to the caller, you have a few choices. The very simplest in your case is to do something like what you are doing here and "wrapping" the values in a dictionary. You could similarly wrap them in a two-value array (which is a little less good since it relies on an implicit contract between caller and callee that there will be exactly two items in the array).
However, a clean and fairly standard approach here would be to create a small class with only two properties on it, and create, fill in, and return that instance with your pair of values. This arguably uses less runtime overhead than a collection object, and has the nice benefit of being semantically explicit and easy to understand for anyone else looking at it.
(There is yet another way, which involves passing pointers as arguments that are "outparams", but that's only idiomatic in rare circumstances in ObjC and I wouldn't recommend it here.)
There is no way to return a key value pair without a dictionary because that is the definition of the dictionary data structure. From apple docs:
The NSDictionary class declares the programmatic interface to objects that manage immutable associations of keys and values
You access the value with
[myDictionary objectForKey:#"myKey"];
If you want to use the returned key-value pair in another dictionary
NSMutableDictionary *otherDict = [NSMutableDictionary alloc] init];
[otherDict setObject:[myDictionary objectForKey:#"myKey"] forKey:#"myKey"];

Sorting objective-C array: where does 'element' come from?

I'm reading a book on Objective-C. A part dealing with sorting arrays in an Addressbook. The sort method is like this:
- (void) sort
{
[book sortUsingSelector: #selector(compareNames:);
}
The 'compareNames' method is like this:
//Compare the two names from the specified address cards
-(NSComparisonResult) compareNames: (id) element
{
return [name compare: [element name]];
}
I kinda can see how it works. By comparing each element of the array to the next until all the 'names' are swapped in alphabetical order.
What I don't get (as it hasn't been mentioned earlier) is where the '(id) element' argument comes from? The 'name' I get cause that's been initialized earlier. But where does 'element' come from? Is that a fixed object that comes with NSArray? Earlier is described how to print an element by e.g. NSLogging nummer[1]. In case of this address book one element or card in that book could be printed by NSLogging book[32]. But how does 'element' fit in that?
The 'name' I get cause that's been initialized earlier.
I'm not sure you understand how this works. The method corresponding to the sorting selector must compare two objects that are members of the array. No "static" objects, no ahead-of-time-initialized stuff, nothing.
This method will be called on an object in the array, and its argument will be another object in the array, to which it should compare the first object (its self argument). The implementation of sortUsingSelector: uses the return value of this method to decide if two objects are in the right order. Internally, it is called like this:
id oneObject = array[firstIndex];
id otherObject = array[secondIndex];
NSComparisonResult cmpRes =
[oneObject performSelector:#selector(comparisonSelector)
withObject:otherObject];
if (cmpRes == NSOrderedDescending) {
// swap the objects if they're in the wrong order
}
Maybe the asymmetry is confusing; in principle, NSArray could as well use a freestanding function that takes two objects and returns the comparison result. Actually, there indeed is a method (sortUsingComparator:) that takes a block which isn't called "on an object" (since it's not a method), but it's simply called with two arguments: the two objects to compare.
It's pretty simple. The sorting algorithm is based on object-comparison. Let's say we have 3 objects in the array: [A, B, C].
Then the method compareNames: is called on the following objects:
[A compareNames:B]
[B compareNames:C]
[A compareNames:C]
The element is just some other object in the array.

Overriding isEqual: so that different objects can equal the same object (sort of)

So I have a situation where im using a class as a kind of struct. now i want to override the isEqual: method so if this type of object is inside an array, i can use [objects indexOfObject:obj]
but, now say obj contains objects called duck, and chicken, i would like to go [objects indexOfObject:duck] and it will actually give me the index of obj
so i tried something like this:
- (BOOL)isEqual:(id)anObject {
if([anObject isKindOfClass:[Duck class]]){
return [anObject isEqual:_duck];
}
else if([anObject isKindOfClass:[Chicken class]]){
return [anObject isEqual:_chicken];
}
return [anObject isEqual:self];
}
which doesnt work and isnt even getting called... think that has to do with overriding hash, which i tried by just returning self.hash (effectively obj.hash) but that didnt work.
Do you think something like this is possible? or should i just use for loops to search through all my obj's to find which duck is contained in which obj and return the index of it (which i can do, just wanted to try be cool and neat at the same time)
It sounds using -isEqual: is a bad idea here.
You can't have a DuckAndChicken that compares equal to its Duck and its Chicken, (and vice versa) because in order to stay consistent, all ducks and chickens would then have to compare equal.
Example:
duck1 + chicken1 compares equal to duck1
chicken1 compares equal to duck1 + chicken1
duck2 + chicken1 compares equal to chicken1
duck2 compares equal to duck1
Universe explodes
The good news is that you don't have to use -indexObject: to retrieve from the array. NSArray has a handy -indexOfObjectPassingTest: method that should do what you want.
[ducksAndChickens indexOfObjectPassingTest:^BOOL(DuckAndChicken *obj, NSUInteger idx, BOOL *stop) {
if ([obj.duck isEqual:myDuck]) {
// woop woop...
}
}];
Yes, it sounds like a hash issue.
I would avoid trying to do this by overriding isEqual: because you're liable to break things that you don't think you'd break.
Instead add a custom method that you can call to determine your version of equivalence. Then have a helper method or a category on NSArray which adds my_indexOfObject: and does the iteration.
Its hard to tell what you really want to do from your example but could you perhaps have two dictionaries one where ducks are the key and one where chickens are the key and the object value is either the actual parent object or a NSNumber with the index in the array. This would make lookup much quicker though would take up more memory, and could make synchronisation between the three data structs.

Implementing a category within the implementation of another interface in Obj-C

I have a custom class in Obj-C called RouteManager which contains an array of NSStrings. Each string is a bus stop name which is used as a key for a dictionary to get the rest of the information for the bus stop (basically, just [busStopDictionary allkeys]). In one of the situations where my app uses this array, I want to return the array sorted by the distance from the user. I've started setting up the code to be able to call sortedArrayUsingSelector on my array with the following method:
- (NSComparisonResult)compareByDistance:(NSString*) otherStop
{
// Return appropriate NSOrdered enum here based on comparison of
// self and otherStop
}
My problem is that in the case where compareByDistance is a method of RouteManager, self refers to the instance of RouteManager. However, I need self to refer to the NSString that the compare is being called on. So, I assumed I needed to setup a category, as such:
#interface NSString (Support)
-(NSComparisonResult) compareByDistance:(NSString*)otherStop;
#end
This got my self reference correct, however this comparison uses values from the RouteManager class. When implemented as seen above, the NSString (Support) implementation obviously complains that those values are undeclared.
That should provide enough background info for my question. How do I go about doing this? I would like my category of NSString, which consists solely of the method compareByDistance, to be able to use values from the current instance of my class, RouteManager, which inherits from NSObject. Ideally, I feel as though the category should somehow be within RouteManager. I feel there has to be some way to accomplish this that is cleaner than passing the necessary values into compareByDistance. Thanks in advance for any and all help.
Your best bet would be to define a custom class for a bus stop, instead of storing them as strings and dictionaries.
Make the BusStop class have properties for Name, Location and whatever else. Implement the compareByDistance: method on the BusStop class.
You can still use a dictionary if you need to look them up by name. Just store them with the name as the dictionary's key, and the BusStop object as the dictionary's value.

Using sortUsingSelector on an NSMutableArray

I have used sortUsingSelector to sort an NSMutableArray of custom objects.
Now I'm trying to sort an NSMutableArray containing NSMutableArrays of custom objects.
Can you use sortUsingSelector on an NSMutableArray, or does it only work for custom classes?
If you can use blocks, the most straightforward way using sortUsingComparator:. Otherwise, you'll need to use sortUsingFunction:.
In either case, you are going to need to write a custom block or function that takes two arrays as arguments and returns a sort order based on their contents (I'm not sure what logic you are using to determine if array A or array B is "before" or "after" the other).
You'd do something like:
static NSInteger MySorterFunc(id leftArray, id rightArray, void *context) {
... return ascending/descending/same based on leftArray vs. rightArray ...
}
Then:
[myArrayOfArrays sortUsingFunction: MySorterFunc context: NULL];
It sends the selector to the objects, so you'll need to use one of the other sorters. Probably sortUsingFunction:context:.
Of course you can also use sortUsingSelector:, it really doesn’t matter whats the object in your array as long as it responds to the selector you want to use. But NSMutableArray and NSArray don’t have any comparison methods themselves, so you’d have to extend them using a category to implement your compare method.
So you probably want to use the other sorting methods pointed out in the other answers here. It’s not impossible to use sortUsingSelector: but it is rather inconvenient and most people (including me) would argue that it’s bad style to write a category for that.