Sorting an array of Objective-c objects - iphone

So I have a custom class Foo that has a number of members:
#interface Foo : NSObject {
NSString *title;
BOOL taken;
NSDate *dateCreated;
}
And in another class I have an NSMutableArray containing a list of these objects. I would very much like to sort this array based on the dateCreated property; I understand I could write my own sorter for this (iterate the array and rearrange based on the date) but I was wondering if there was a proper Objective-C way of achieving this?
Some sort of sorting mechanism where I can provide the member variable to sort by would be great.
In C++ I used to overload the < = > operators and this allowed me to sort by object, but I have a funny feeling Objective-C might offer a nicer alternative?
Many thanks

That's quite simple to do.
First, in your Foo object, create a method
- (NSComparisonResult) compareWithAnotherFoo:(Foo*) anotherFoo;
Which will return
[[self dateCreated] compare:[anotherFoo dateCreated]];
In the end, call on the array
[yourArray sortUsingSelector:#selector(compareWithAnotherFoo:)];
Hope this helps,
Paul

Related

More efficient way to iterate through an array of NSStrings and compare them

I'm just looking for a nicer and more efficient way to iterate through a given array of objects and compare a NSString property of each to another array just containing NSStrings.
My current code uses two for-each loops but it don't think that it is the most efficient way.
for (MYClass *foo in arrayOfMyClass) {
for (NSString *ID in arrayOfStringIDs) {
if ([foo.Id isEqualToString:ID]) {
//Do something
break;
}
}
}
I think that it should be somehow possible to drop at least one loop with some cool tricks.
If all you want to know is if foo.Id exists in arrayOfStringIDs, use an NSSet of strings instead. Then you can do:
NSSet * mySetOfStringIDs = [NSSet setWithArray:arrayOfStringIDs];
for(MyClass * foo in arrayOfMyClass) {
if([mySetOfStringIDs containsObject:foo.Id]) {
// Do something
break;
}
}
This avoids the second loop, since containsObject: is generally much faster than O(n) for a set. You should, of course, do your own profiling as needed.
Check for indexofobject method of Nsarray. May be it can help you to get the index directly instead of a loop for the string in nsarray.
If you want to get an array of strings that exist in both arrayOfMyClass and arrayOfStringIDs then you could use key-value coding to pull the set of strings out of arrayOfMyClass and intersect the resulting set with arrayOfStringIDs. If your class is KVC compliant then you can get all the Id strings out of it as a set:
NSMutableSet *idSet=[NSMutableSet setWithArray:[arrayOfMyClass
valueForKeyPath:#"#distinctUnionOfObjects.Id"]];
[idSet intersectSet:[NSSet setWithArray:arrayOfStringIDs]];
NSArray *idArray=[idSet allObjects];
Unfortunately there is not a method to intersect two NSArrays which is why they have to be turned into a set first.

for loops - Object type disregarded?

I sometimes like to organize IB elements into NSArrays primarily to help me organize my elements. Most often, different classes of objects make it into the same array with each other. While this is a convenient way of organization, I can't seem to wrap my head around why if I have an array like this:
NSArray *array = [NSArray arrayWithObjects:((UITextField *)textField), ((UISegmentedController *)segmentedController), nil];
Why I get "Does not respond to selector" messages when I put a for loop like this:
for (UITextField *text in array) {
[text setText:#""];
}
The for loop seems to be passed objects that are not of class UITextField.
What is the point of declaring the object's class if all objects in the specified array are passed through the loop?
EDIT Just for reference, this is how I'm handling it as of now:
for (id *object in array) {
if ([object isMemberOfClass:[UITextField class]]) {
foo();
} else if ([object isMemberOfClass:[UISegmentedController class]) {
bar();
}
}
When you do
for (UITextField *text in...
the object pointers from the array are cast to UITextField* type - so if the object isn't actually a UITextField, all sorts of weird things may happen if you try to call UITextField methods.
So instead use the id type (no * needed, btw):
for (id obj in array)
Then check the type as you do and call the appropriate methods. Or, filter the array to get only objects of a certain type, then go though that type only:
for (UITextField* text in [array filteredArrayUsingPredicate:...])
Edit: here's how to build class filter predicates:
Is it possible to filter an NSArray by class?
What is the point of declaring the object's class if all objects in the specified array are passed through the loop?
The class name is just there to let the compiler know what it should expect to find. This allows it to try to figure out what methods it should expect you to call and how you might treat the object. It's the same idea as passing in an int to a method that takes float. The method will not ignore ints - it's assuming you're passing the correct type. You're just giving this construct a little more credit than it's due:
for (UITextField *text in array)
It just doesn't have that functionality. How you're handling it now is the correct way.
Are you sure you don't get an error when you run that code? The "does not respond to selector" message is a runtime error, not a compile time error. The compiler has no idea whether the objects in the array implement -setText:, but you should certainly get an error when you actually send that message to an instance of UISegmentedControl.
Another possibility is that you've got a class called UISegmentedController that does have a -setText: method. The name of the class that implements the multi-part bar-graph-looking user interface widget is UISegmentedControl. So either the code you're showing isn't real, tested code, or you've got a class that we don't know about.

sorting mutable array in alphabetical order

I am having a nsmutable array and in that nearly 50 -60 object having different names ,and can i sort this array in alphabetical order (Is it possible, How?)
For a simple sort like this, I like to use sort descriptors.
Suppose you have an mutable array of objects whose class has a name NSString property:
NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:NO];
[myArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];
Absolutely, you can use sortUsingSelector: for this:
[myArray sortUsingSelector:#selector(compare:)];
If your array has custom objects, then you will need to implement a sorting method on those objects:
#implementation myCustomObject
...
-(NSComparisonResult) compare:(myCustomObject*) other {
return [self.name compare:other.name];
}
#end
TechZen's approach works well, but it would work better if you used NSSortDescriptor's +sortDescriptorWithKey:ascending:selector:, passing "localizedCompare:" as the selector. This way, the sorting is localized to the user's language, which can make a big difference in string comparison.
myArray=[myDict keysSortedByValueUsingSelector:#selector(compare:)];
Simply Worked for me!

dynamically creating variable names in objective-c

i'm in a bit of a situation here...
i am passing a string to a function and in that function i need to create an array whose name is the value of the string.
Say, for example: -(void) function : (NSString *) arrayName; //let arrayName = #"foo";
In this function I need to create an array named "foo" i.e the value of the passed parameter.
Can anyone help please :|
Thanks in advance ;)
Arrays don't have names. Variables have names, but variables are local to their scope, so once you leave the scope of that method, having a variable named "foo" is pointless; you can name the variable whatever you want and it will work just fine. Ex:
- (void) function:(id)whatever {
NSArray * myVariable = [NSArray arrayWithStuff....];
//use myVariable
}
What are you really trying to do?
That is not possible in Objective-C, but you can use e.g. a dictionary that maps a string to an array.
E.g. assuming something like the following property:
#property (readonly, retain) NSMutableDictionary *arrays;
... you can store an array by name:
- (void)function:(NSString *)arrayName {
NSArray *array = [NSArray arrayWithObjects:#"foo", #"bar", nil];
[self.arrays setObject:array forKey:arrayName];
}
... and access it like so:
NSArray *array = [self.arrays objectForKey:arrayName];
C is a compiled language where any source code names (for variables, functions, etc.) are not available at runtime (except for perhaps optionally debugging, -g). The Objective C runtime adds to this the ability to look up Obj C methods and classes by name, but not objects, nor any C stuff. So you're out of luck unless you build your own mini-language-interpreter structure for reference-by-name. Lots of ways to do this, but simple languages usually build some sort of variable table, something like a dictionary, array, or linked-list of objects (structs, tuples, etc.) containing string name, object pointer (maybe also type, size, etc.).

Property/Return value for this variable?

I have a variable declared like this in a class:
Entity *array[BOARD_SIZE][BOARD_SIZE];
I need to set up either a #property that allows me to access (read) the array elements, or a function that returns a reference so I can access the array elements.
- ( ??? ) getEntityArray
{
return ???;
}
or
#property (????) Entity ??? ;
I really need to work with the array declared in this way.
Any advice?
Thanks!
Don't do this. Use an NSArray to store Objective-C objects.
Have a look at the answer to this question if you want to know how to handle multi-dimensional arrays of primitives:
Add 2d int array to NSDictionary
While Rob's answer is valid, the question itself is interesting. Objective C is just a layer on top of standard C, so a question about returning array types is valid.
The answer to the question is pretty easy: it's not possible to return array types in C. You can only return pointers. The problem with returning a pointer is that the dimensions of the multidimensional array are unknown to the caller, so you can't use the returned value as a multidimensional array.
You could do one of the following - use a typedef to make it easier to handle or not.
in your .h file:
#define BOARD_SIZE 32
typedef NSString *EntityArrayPtr[BOARD_SIZE][BOARD_SIZE];
#interface EntityTestWithNSString : NSObject {
EntityArrayPtr *x;
NSString *tempStrs[15][15];
}
#property NSString **tempStrs;
#end
in your .m file:
- ( EntityArrayPtr *) getEntityArray
{
return x;
}
- (NSString **) getStringArrays
{
return &tempStrs[0][0];
}
I just compiled this and it compiles. I'll leave it to you to see which works best for your situation.
You could make methods that grab you the one entity at the index1.index2 that you need...
I definitely would make methods to return to you exactly the object you need, instead of handing off an array pointer and letting some other object mess around with it.