I'm trying to sort an array, filled with objects from a class i wrote. The class i wrote contains an NSString itself, as the "name". So what i wanna do, is to sort the array, based on the name property the objects contain. I tried the simple:
[anArray sortedArrayUsingSelecter: CaseInsensitiveCompare];
But as you can guess, it sends the caseInsensitiveCompare message to the objects itself, and it won't respond to that one.
So i'm guessing i have to make my objects able to respond to the caseInsensitiveCompare? Not quite sure how to write such a method, so any pointers would be lovely.
Thanks
You can use the method sortedArrayUsingComparator:
NSArray *sortedArray = [anArray sortedArrayUsingComparator:^(MyClass *a, MyClass *b) {
return [a.name caseInsensitiveCompare:b.name];
}];
You can sortedArrayUsingComparator:(NSComparator)cmptr (NSArray reference) to sort the array. For instance, if you want to sort by the name property, do the following (assuming your class is called Person):
NSArray *sortedArray = [anArray sortedArrayUsingComparator:^(id a, id b) {
NSString *first = [(Person *)a name];
NSString *second = [(Person *)b name];
return [first caseInsensitiveCompare:second];
}];
Related
I am having a problem that I think I am overcomplicating.
I need to make either an NSMutableArray or NSMutableDictionary. I am going to be adding at least two objects like below:
NSMutableArray *results = [[NSMutableArray alloc] init];
[results addObject: [[NSMutableArray alloc] initWithObjects: [NSNumber numberWithInteger:myValue01], #"valueLabel01", nil]];
This gives me the array I need but after all the objects are added I need to be able to sort the array by the first column (the integers - myValues). I know how to sort when there is a key, but I am not sure how to add a key or if there is another way to sort the array.
I may be adding more objects to the array later on.
Quick reference to another great answer for this question:
How to sort NSMutableArray using sortedArrayUsingDescriptors?
NSSortDescriptors can be your best friend in these situations :)
What you have done here is create a list with two elements: [NSNumber numberWithInteger:myValue01] and #"valueLabel01". It seems to me that you wanted to keep records, each with a number and a string? You should first make a class that will contain the number and the string, and then think about sorting.
Doesn't the sortedArrayUsingComparator: method work for you? Something like:
- (NSArray *)sortedArray {
return [results sortedArrayUsingComparator:(NSComparator)^(id obj1, id obj2)
{
NSNumber *number1 = [obj1 objectAtIndex:0];
NSNumber *number2 = [obj2 objectAtIndex:0];
return [number1 compare:number2]; }];
}
I am using one NSMutableArray with same string object.
Here is the code
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSObject *obj = [arr objectAtIndex:2];
[arr removeObject:obj];
NSLog(#"%#",arr);
When i try to remove 3rd object of array, its removing all object with "hi" string.
I am not getting why its happening.
my doubt is while removing object, NSMutableArray match string or address.
It's because you're using removeObject which removes all objects that are "equal" to the one you pass in. As per this Apple documentation:
This method uses indexOfObject: to locate matches and then removes
them by using removeObjectAtIndex:. Thus, matches are determined on
the basis of an object’s response to the isEqual: message. If the
array does not contain anObject, the method has no effect (although it
does incur the overhead of searching the contents).
You're seeing the effects of literal strings here where each of those #"hi" objects will turn out to be the same object just added many times.
What you really want to do is this:
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
[arr removeObjectAtIndex:2];
NSLog(#"%#",arr);
Then you're specifically removing the object at index 2.
According to https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
removeObject:
Removes all occurrences in the array of a given object.
which is exactly the behaviour you're seeing. If you want to remove the object at a particular position, you want removeObjectAtIndex:.
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSUInteger obj = [arr indexOfObject:#"hi"]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj]; //removes the object from the array
NSLog(#"%#",arr);
I am facing a very regular scenario.
I have an NSArray which has object of a custom type, say Person. The Person class has the attributes: firstName, lastName and age.
How can I get an NSArray containing only one attribute from the NSArray having Person objects?
Something like:
NSArray *people;
NSArray *firstNames = [people getArrayOfAttribute:#"firstName" andType:Person.Class]
I have a solution of writing a for loop and fill in the firstNames array but I don't want to do that.
NSArray will handle this for you using KVC
NSArray *people ...;
NSArray *firstName = [people valueForKey:#"firstName"];
This will give you an array of the firstName values from each entry in the array
Check out the filterUsingPredicate: method in NSMutableArray, basically you create a NSPredicate object that will define how the array will be filtered.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html#//apple_ref/doc/uid/TP40001794-CJBDBHCB
This guide will give you an overview, and has a section for dealing with arrays.
You can also use block based enumeration:
NSArray *people; // assumably has a bunch of people
NSMutableArray *firstNames = [NSMutableArray array];
[people enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL*flag){
// filter however you want...
[firstNames addObject:[Person firstName]];
}];
The benefit is it is fast and efficient if you have a bunch of people...
i have created NSMutableDictionary with 10 keys.Now i want to access NSMutableDictionary keys in a same order as it was added to NSMutableDictionary (using SetValue:* forKey:* );
How can i achieve that ?
If you absolutely must use a dictionary container, you have to use a key that is sortable by the order in which you add key-value pairs. Thus, when creating your dictionary, you use a key that is an auto-incrementing integer or similar. You can then sort on the (integer) keys and retrieve the values associated with those keys.
If you do all of that, however, you may as well just use an NSMutableArray and add values to the array directly! It will be much faster and require less code. You just retrieve objects in order:
for (id obj in myArray) { /* do stuff with obj... */ }
NSMutableDictionary can't do that. Take a look at e.g. Matt Gallaghers OrderedDictionary.
I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.
- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [referenceArray count]; i++)
{
if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
{
[returnArray addObject:[arrReference objectAtIndex:i]];
}
}
return [returnArray copy];
}
Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.
All that said, if you're trying to do something simple, it should work nicely. In your case, you could build up the reference array as you are looping through your setValue:forKey:.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (//whatever you are looping through here)
{
[yourDictionary setValue://whatever forKey:key];
[referenceArray addObject:key];
}
Then, when you want to loop over your items in the order they came in, you just
for (NSString *key in [self reorderArray:[myDict allKeys] toArray:referenceArray])
Actually you have a reference array in order manner then why you have to add to one more array.So i guess this approach is not good.Please consider my opinion.
Although #GenralMike 's answer works a breeze, it could be optimized by leaving off the unnecessary code as follows:
1) Keep an array to hold reference to the dictionary keys in the order they are added.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (id object in someArray) {
[yourDictionary setObject:object forKey:someKey];
[referenceArray addObject:someKey]; // add key to reference array
}
2) Now the "referenceArray" holds all of the keys in order, So you can retrieve objects from your dictionary in the same order as they were originally added to the dictionary.
for (NSString *key in referenceArray){
//get object from dictionary in order
id object = [yourDictionary objectForKey:key];
}
There's the option to go the long way, if an receiver class conforms to the NSKeyValueProtocol:
[myInstance setValue:[NSNumber numberWithInt:2] forKey:#"integerProperty"];
or the short way:
myInstance.integerProperty = 2;
what's the point of this KVC method? When is this useful?
First, those aren't the same, the second should be:
myInstance.integerProperty = [NSNumber numbwerWithInt:2];
if integerProperty is an NSNumber.
In general you use the second form when you are doing the most things. You use setValue:forKey: and valueForKey: when you want to dynamically choose the property to store things in. For instance, think about how valueForKeyPath: against an NSArray works (for reference, if you call -valueForKey: against an NSArray it will return an array where each object is the result of asking the corresponding object in that NSArray for that value:
- (NSArray *) valueForKey:(id)key {
NSMutableArray *retval = [NSMutableArray array];
for (NSObject *object in self) {
[retval addObject:[object valueForKey:key]];
}
return retval;
}
In the above case we were able to use valueForKey: to implement our function even though we do not know what the key is beforehand, since it is passed in as an argument.