I’m making a languages application, and I have a long list of vocabulary relating to that language (German, in case anyone was interested). I have the functionality in my app to switch between sorting the tableview by German words, or by english words.
When I use the following:开发者_C百科
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [[string componentsSeparatedByString:@"\n"] sortedArrayUsingSelector:@selector(compare:)];
it works absolutely perfectly - by which I mean, exactly as expected. What I would like to improve on this however, is that there are certain words, such as verbs or nouns, which are always preceded by prefixes, like “to”, as in “to do something”, or “the” in front of nouns. So what I would like to do is somehow exclude these from my sort, because otherwise I end up with all the verbs being sorted alphabetically under the “t” section in my array, which is not very user friendly.
I’ve looked through the Apple documentation about NSString and NSArray, as this is where the compare
function is (unless I’m very much mistaken), and I haven’t found any way that makes sense to me. This is the first time I have done any data handling like this so I may be missing something simple, and so I would really appreciate some help.
Thanks very much
Michaeljvdw
You're on the right track. What you want to use instead of the (built-in) compare
method is to write your own method, which can eliminate the "to" or "the" bits if they exist, and then use the existing compare
method.
Your call would look something like this:
NSArray *array = [[string componentsSeparatedByString:@"\n"] sortedArrayUsingSelector:@selector(myCompare:)];
Using a custom category you give to NSString
with the following methods:
// This method can be exposed in a header
- (NSComparisonResult)myCompare:(NSString*)aString
{
NSString* selfTrimmed = [self removeArticles];
NSString* aStringTrimmed = [s2 removeArticles];
return [self compare:aString];
}
// This method can be kept private in the .m implementation
- (NSString*)removeArticles
{
NSRange range = NSMakeRange(NSNotFound, 0);
if ([self hasPrefix:@"to "])
{
range = [self rangeOfString:@"to "];
}
else if ([self hasPrefix:@"the "])
{
range = [self rangeOfString:@"the "];
}
if (range.location != NSNotFound)
{
return [self substringFromIndex:range.length];
}
else
{
return self;
}
}
You might have some luck with localizedCompare: or localizedStandardCompare:, but I don't think that either of these will strip out articles and prepositions like you want. Instead, you will probably have to define a category on NSString
that provides the specific style of sorting you're looking for:
@interface NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other;
@end
@implementation NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other {
NSMutableString *mutableSelf = [NSMutableString stringWithString:self];
[mutableSelf
replaceOccurrencesOfString:@"das"
withString:@""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [mutableSelf length])
];
...
// delete articles from 'other' too
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmedSelf = [mutableSelf stringByTrimmingCharactersInSet:trimSet];
NSString *trimmedOther = ...;
return [trimmedSelf localizedCaseInsensitiveCompare:trimmedOther];
}
@end
You can then use @selector(compareWithoutArticles:)
as your sort selector for NSArray
.
First, don't use compare:
. Use localizedCompare:
instead. This is important, because whether á
appears just after a
or after z
as a separate letter depends on the language. localizedCompare:
takes care of that.
--edit
As Justin says, localizedStandardCompare:
is the selector to be used! I didn't know that method. As written in the documentation, localizedStandardCompare:
does more than localizedCompare:
, although the document doesn't say exactly what it does.
--end of edit
If you want more, you need to implement that yourself. You can use category for that purpose. First declare it
@interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string;
@end
and then implement it
@interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string{
...
}
@end
This way you can add methods to an existing class. Then you can use
NSArray *array = [[string componentsSeparatedByString:@"\n"]
sortedArrayUsingSelector:@selector(michaelsSuperCompare:)];
It is important to prefix the method name with something distinctive, not to accidentally crash with internal methods used by Apple.
As for the functionality, you need to implement that yourself, as far as I know. You can get the current locale with [NSLocale currentLocale]
. You can implement a nicer behavior for the languages you know, and then default to localizedCompare:
for unknown languages.
I would somehow do -replaceOccurancesOfStrings on all the data eg "To" -> "" - and then reload the data. (or this can in a text editor)
Another thing to think about is having eg 'to walk' changed to 'walk (to)' which can be done ahead of time (and will also create less confusion for the user as they are scrolling alphabetically).
精彩评论