NSSortDescriptor sorting by alpha numeric - iphone

I'm trying to sort results from a CoreData "table" of "Tracks" in a similar manner to iTunes. The problem is, "ASC" sort uses the first characters to sort so I end up with:
(I Can't Get No) Satisfaction
A Hard Days Night
I'd like The Stones to show up in the results with "I", basically ignorning anything ^A-Za-z0-9. I've tried a custom selector and comparator block but it just ignores it so I'm stuck.

From my experience you're better off having a sortName attribute that you generate on object creation. You can then use that key to sort your CoreData results in a much simpler and faster fashion.

Another solution would be to sort manually after fetching the results:
[tracks sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
NSError *error = nil;
NSString *pattern = #"[^A-Za-z0-9]";
NSRegularExpression *expr = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:&error];
NSString *title1 = [(Track *)obj1 title];
NSString *title2 = [(Track *)obj2 title];
NSString *title1Match = [expr stringByReplacingMatchesInString:title1
options:0
range:NSMakeRange(0, [title1 length])
withTemplate:#""];
NSString *title2Match = [expr stringByReplacingMatchesInString:title2
options:0
range:NSMakeRange(0, [title2 length])
withTemplate:#""];
return [title1Match compare:title2Match options:NSCaseInsensitiveSearch];
}];
I tried [\W] as the pattern as well but seemed like there was a huge performance hit.

Related

How to find occurrence of a format in NSString

I am reading a feed, and need to find the occurrence of a string format, for example, in "Feed for 03/02/2012". Now this date will always be of the format: %#/%#/%#. I need to find is this format occurs, and thus extract the date. The date and month can be any, so i don't have a specific date to search for. I searched in the docs, could not find it. Only option possible to me seems to take all 12 combinations of a month like "/12/", and find the occurrence of any of these.
Is there any better approach?
You can use regular expressions:
NSString *feed = #"Feed for 03/02/2012";
NSError *error;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:#"([0-9]{1,2})/([0-9]{1,2})/([0-2][0-9]{3})"
options:NSRegularExpressionCaseInsensitive
error:&error];
[regex enumerateMatchesInString:feed options:0 range:NSMakeRange(0, [feed length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSString *month = [feed substringWithRange:[match rangeAtIndex:1]];
NSString *day = [feed substringWithRange:[match rangeAtIndex:2]];
NSString *year = [feed substringWithRange:[match rangeAtIndex:3]];
//...
}];

Take part of string in-between symbols?

I would like to be able to take the numbers lying behind the ` symbol and in front of any character that is non-numerical and convert it into a integer.
Ex.
Original String: 2*3*(123`)
Result: 123
Original String: 4`12
Result: 4
Thanks,
Regards.
You can use regular expressions. You can find all the occurrences like this:
NSString *mystring = #"123(12`)456+1093`";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"([0-9]+)`" options:0 error:nil];
NSArray *matches = [regex matchesInString:mystring options:0 range:NSMakeRange(0, mystring.length)];
for (NSTextCheckingResult *match in matches) {
NSLog(#"%#", [mystring substringWithRange:[match rangeAtIndex:1]]);
}
// 12 and 1093
If you only need one occurrence, then replace the for loop with the following:
if (matches.count>0) {
NSTextCheckingResult *match = [matches objectAtIndex:0];
NSLog(#"%#", [mystring substringWithRange:[match rangeAtIndex:1]]);
}
There can be better way to do this, Quickly i could come up with this,
NSString *mystring = #"123(12`)";
NSString *neededString = nil;
NSScanner *scanner =[NSScanner scannerWithString:mystring];
[scanner scanUpToString:#"`" intoString:&neededString];
neededString = [self reverseString:neededString];
NSLog(#"%#",[self reverseString:[NSString stringWithFormat:#"%d",[neededString intValue]]]);
To reverse a string you can see this

returning all index of a character in a NSString

IS there a method that would return all the index of the occurences of the letter 'a' in a NSString lets say? Tried looking at the documentation and there seems that there isn't any. So I might have to break the NSString to an NSArray of chars and iterate?
Try [NSRegularExpression enumerateMatchesInString:options:range:usingBlock:]. Or indeed, any of the other NSRegularExpression matching methods. They won't return an NSIndexSet - it'll be an array of NSTextChecking objects - but you can quite easily get the index out of that.
Here's some (untested!) sample code:
NSString* aString = #"Here's a string, that contains some letters a";
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:#"a" options:0 error:NULL];
NSArray* matches = [regex matchesInString:aString options:0 range:NSMakeRange(0,[aString length])];
for(NSTextCheckingResult* i in matches) {
NSRange range = i.range;
NSUInteger index = range.location; //the index you were looking for!
//do something here
}
It's actually more efficient to use enumerateMatchesInString, but I don't know how familiar you are with Blocks, so I opted for the more common fast enumeration of an NSArray.
Update: the same code using Blocks.
NSString* aString = #"Here's a string, that contains some letters a";
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:#"a";
[regex enumerateMatchesInString:aString
options:0
range:NSMakeRange(0,[aString length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange range = result.range;
NSUInteger index = range.location; //the index you were looking for
//do work here
}];
NSString *full_string=#"The Quick Brown Fox Brown";
NSMutableArray *countloc=[[NSMutableArray alloc]init];
int temp=0;
int len=[full_string length];
for(int i =0;i<[full_string length];i++)
{
NSRange range=[full_string rangeOfString:#"Brown" options:0 range:NSMakeRange(temp,len-1)];
if(range.location<[full_string length])
[countloc addObject:[NSString stringWithFormat:#"%d",range.location]];
temp=range.location+1;
len=[full_string length]-range.location;
i=temp;
}
Here searching for the substring Brown and
Location of the substring is stored in the array countloc

Text extraction with NSRegularExpression

Given a NSString *test = #"...href="/functions?q=KEYWORD\x26amp...";
How can I extract the word KEYWORD from the string using NSRegularExpression?
I have tried with the following NSRegularExpression on iOS SDK 4.2 but it is not able to find the text. Does the following code looks okay?
NSRegularExpression *testRegex = [NSRegularExpression regularExpressionWithPattern:#"(?<=href=\"\\/functions\\?q=).+?(?=\\x26amp])" options:0 error:nil];
NSRange result = [testRegex rangeOfFirstMatchInString:test options:0 range:NSMakeRange(0, [test length])];
You have a stray "]" in your regex, right before the end, which is probably causing a problem. You also need to use four slashes to match a slash in the input string. (Double it to escape it in the C string, and then double again to escape it in the regex). I'd suggest two things. First, pass something in the error parameter and take a look at in it in the debugger. Second, I'm not a big fan of lookahead/lookbehind expressions. I think this style is more readable:
NSString *regexStr = #"href=\"\\/functions\\?=(.+?)\\\\x26amp";
NSError *error;
NSRegularExpression *testRegex = [NSRegularExpression regularExpressionWithPattern:regexStr options:0 error:&error];
if( testRegex == nil ) NSLog( #"Error making regex: %#", error );
NSTextCheckingResult *result = [testRegex firstMatchInString:test options:0 range:NSMakeRange(0, [test length])];
NSRange range = [result rangeAtIndex:1];

NSRegularExpression and capture groups on iphone

I need a little kickstart on regex on the iphone.
Basically I have a list of dates in a private MediaWiki in the form of
*185 BC: SOME EVENT HERE
*2001: SOME OTHER EVENT MUCH LATER
I now want to parse that into an Object that has a NSDate property and a -say- NSString property.
I have this so far: (rawContentString contains the mediawiki syntax of the page)
NSString* regexString =#"\\*( *[0-9]{1,}.*): (.*)";
NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive;
NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:regexString options:options error:&error];
if (error) {
NSLog(#"%#", [error description]);
}
NSArray* results = [regex matchesInString:rawContentString options:0 range:NSMakeRange(0, [rawContentString length])];
for (NSTextCheckingResult* result in results) {
NSString* resultString = [rawContentString substringWithRange:result.range];
NSLog(#"%#",resultString);
}
unfortunately I think the regex is not working the way I hope and I dont know how to capture the matched date and text.
Any help would be great.
BTW: there is not by any chance a regex Pattern compilation for MediaWiki Syntax out there somewhere ?
Thanks in advance
Heiko
*
My issue was that I was using matchesInString and I needed to use firstMatchInString because it returns multiple ranges in a single NSTextCheckingResult.
This is counter intuitive, but it worked.
I got the answer from http://snipplr.com/view/63340/
My Code (to parse credit card track data):
NSRegularExpression *track1Pattern = [NSRegularExpression regularExpressionWithPattern:#"%.(.+?)\\^(.+?)\\^([0-9]{2})([0-9]{2}).+?\\?." options:NSRegularExpressionCaseInsensitive error:&error];
NSTextCheckingResult *result = [track1Pattern firstMatchInString:trackString options:NSMatchingReportCompletion range:NSMakeRange(0, trackString.length)];
self.cardNumber = [trackString substringWithRange: [result rangeAtIndex:1]];
self.cardHolderName = [trackString substringWithRange: [result rangeAtIndex:2]];
self.expirationMonth = [trackString substringWithRange: [result rangeAtIndex:3]];
self.expirationYear = [trackString substringWithRange: [result rangeAtIndex:4]];
As for the regex, i think something around these lines:
\*([ 0-9]{1,}.*):(.*)
should work better to what you need. You're not escaping the first *, and why is there a * in the first group statement?