views:

1166

answers:

4

This is confusing to me. I have a function that does this:

void ListAllStoredLocations(NSString *SearchTerm){  
NSMutableDictionary *item;  
NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"/Preferences/yourprogram.plist"];

item = [[[NSMutableDictionary alloc] initWithContentsOfFile:filePath] mutableCopy]; 

NSMutableArray *ReadStoredArray = [item objectForKey:SearchTerm];
NSMutableArray *SortedArray = [[NSMutableArray alloc] init];
NSString *CurrentResult=@"";

for (int i = 0; i< [ReadStoredArray count]; i++){
    CurrentResult=(NSString *)[ReadStoredArray objectAtIndex:i];
    [SortedArray addObject:CurrentResult];
}

[SortedArray sortUsingSelector:@selector(compare:)];

for (int i = 0; i< [SortedArray count]; i++){
    NSLog(@"%@",[SortedArray objectAtIndex:i]);
}


[item release];

}

Which finds outputs NSStrings in the first for loop like this:

Location1

Location2

Not a location

Location2

Location3

Location2

and I want the output to be alphabetical:

Location1

Location2

Location2

Location2

Location3

Not a location

But, no matter what, the "[SortedArray sortUsingSelector:@selector(compare:)];" just does not sort the array. Nothing happens.

Maybe I'm going about this all wrong, but every example that I've seen online sorts NSStrings like this - so I don't know what to do.

My endgame, if there is a better solution out there is to output the count of the greatest repeated entry. I was thinking that sorting would be a step in that direction.

Really, what I'm looking for is this as output:

Location2

Because "location2" has the most duplications in that list.

Any help?

A: 

To start, you are leaking memory like crazy. Learn the rules: if you create an object (alloc/init or copy) then you own it and must release it.

item = [[[NSMutableDictionary alloc] initWithContentsOfFile:filePath] mutableCopy];

In that line you are creating a mutable dictionary, then creating a mutable copy of it, losing the original instance. You should replace that with:

item = [[NSDictionary alloc] initWithContentsOfFile:filePath];

You don't actually mutate the dictionary in your code, so I let it be NSDictionary.

Next, the type on this declaration:

NSMutableArray *ReadStoredArray = [item objectForKey:SearchTerm];

is incorrect. Even if the dictionary was mutable, it's members are not guaranteed to be. (mutableCopy is a shallow copy.) Since you don't actually modify that array, let's change the line to:

NSArray *ReadStoredArray = [item objectForKey:SearchTerm];

Now, if you want to copy items from ReadStoredArray into SortedArray, you could replace the loop with

[SortedArray addObjectsFromArray:ReadStoredArray];

But since you're making an exact copy, you could also just write

SortedArray = [ReadStoredArray mutableCopy];

But you don't seem to need SortedArray to be mutable, so you could just call this other form, which returns a new sorted array rather than sorting a mutable array in place:

SortedArray = [ReadStoredArray sortedArrayUsingSelector:@selector(compare:)];

So now your function looks like this:

void ListAllStoredLocations(NSString *SearchTerm) {
    NSDictionary *item;  
    NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"/Preferences/yourprogram.plist"];
    item = [[NSDictionary alloc] initWithContentsOfFile:filePath]; 

    NSArray *ReadStoredArray = [item objectForKey:SearchTerm];
    NSArray *SortedArray = [ReadStoredArray sortedArrayUsingSelector:@selector(compare:)];

    for (int i = 0; i< [SortedArray count]; i++){
        NSLog(@"%@",[SortedArray objectAtIndex:i]);
    }

    [item release];
}

You don't need to release ReadStoredArray or SortedArray because you do not own them (no alloc/init or copy in the calls).

As for your actual question... there's no obvious reason from the code why the sorting would not work. Sorry! A lot of common problems would have triggered exceptions, not silent failures.

If the file did not exist or could not be loaded, initWithContentsOfFile: would have thrown an exception. If ReadStoredArray was nil, then CurrentResult would be nil, and addObject: would throw an exception. If the objects in the array did not respond to the compare: selector, sortUsingSelector: would have thrown an exception. If SortedArray was nil, the code would silently fail, but it would also print no output. (Also, for it to be nil the alloc/init would have to fail, meaning you were out of memory.)

Aside from memory leaks and nontraditional style (starting your variable names with upper case letters) there's nothing apparently wrong with your code. Something is missing.

benzado
"As for your actual question... there's no obvious reason from the code why the sorting would not work. Sorry!" --- ACK dammit.... haha. Well thanks for the pointers on the scripting. I'm totally new to ObjC and its kicking my arse. I'll keep looking.
Andrew J. Freyer
And no, I wasn't omitting anything from my method.This still does not sort the array... this is odd.
Andrew J. Freyer
I updated my answer (near the end) to explain a few common problems that are all ruled out because you didn't say an exception is being thrown.
benzado
It turns out that the arrays that I was passing were only one element arrays. When I printed the result, I inadvertently printed in such a way so as to trick myself into thinking I was printing an entire array. Dumb mistake on my part. Thanks for the help though - memory leaks are something that I'm getting used to in ObjC
Andrew J. Freyer
A: 

I didn't see anything obvious that would prevent your array from sorting, so I would try using a custom myCompare: method as the sorting selector instead of the built-in compare. This will allow you to log each individual comparison, so you know each object is getting compared as it should, and it's returning the correct comparison result.

Add this to your file (above your function definition), then change your function to sort with @selector(myCompare:):

@interface NSString (MyCompare)

- (NSComparisonResult) myCompare: (NSString *) aString;

@end

@implementation NSString (MyCompare)

- (NSComparisonResult) myCompare: (NSString *) aString
{
   NSComparisonResult result;

   result = [self compare: aString];

   NSLog(@"Compared %@ & %@, result: %d", self, aString, (int) result);

   return result;
}

@end
tedge
+2  A: 

Given your array of string looks like this:

NSMutableArray * array = [NSMutableArray array];
[array addObject:@"Location1"];
[array addObject:@"Location2"];
[array addObject:@"Not a location"];
[array addObject:@"Location2"];
[array addObject:@"Location3"];
[array addObject:@"Location2"];

NSLog(@"------------- original:");
for (id obj in array) NSLog(@"%@", obj);

You can sort it like this:

NSLog(@"------------- sorted:");
NSArray * sortedArray =
     [array sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
for (id obj in sortedArray) NSLog(@"%@", obj);

Output:

2010-02-06 00:24:14.915 x[23867:903] ------------- original:
2010-02-06 00:24:14.917 x[23867:903] Location1
2010-02-06 00:24:14.921 x[23867:903] Location2
2010-02-06 00:24:14.922 x[23867:903] Not a location
2010-02-06 00:24:14.922 x[23867:903] Location2
2010-02-06 00:24:14.923 x[23867:903] Location3
2010-02-06 00:24:14.924 x[23867:903] Location2
2010-02-06 00:24:14.924 x[23867:903] ------------- sorted:
2010-02-06 00:24:14.925 x[23867:903] Location1
2010-02-06 00:24:14.926 x[23867:903] Location2
2010-02-06 00:24:14.926 x[23867:903] Location2
2010-02-06 00:24:14.927 x[23867:903] Location2
2010-02-06 00:24:14.927 x[23867:903] Location3
2010-02-06 00:24:14.928 x[23867:903] Not a location

If you want to find the object with the most occurrences, given the original array:

NSCountedSet * set = [[NSCountedSet alloc] initWithArray:array];
for (id obj in set) NSLog(@"%d - %@", [set countForObject:obj], obj);

int count = 0;
int maxc = 0;
id maxobj;
for (id obj in set)
{
    count = [set countForObject:obj];
    if (maxc < count) maxc = count, maxobj = obj;
}

NSLog(@"max is: %d - %@", maxc, maxobj);

Output:

2010-02-06 00:39:46.310 x[24516:903] 1 - Location1
2010-02-06 00:39:46.311 x[24516:903] 1 - Not a location
2010-02-06 00:39:46.311 x[24516:903] 3 - Location2
2010-02-06 00:39:46.312 x[24516:903] 1 - Location3
2010-02-06 00:39:46.313 x[24516:903] max is: 3 - Location2
stefanB
+1 exactly how i'd do it
Dave DeLong
A: 

You can easily sort an array using the API:

[YourArrayObj sortUsingSelector:@selector(compare:)];

After this statement you just print your array objects you'll get the sorted array.

vivek