Silver Challenge Solution: Processing the Reply


#1

At the end of the trimItemTitles I used NSString’s stringByReplacingOccurrencesOfString:withString: method:

// Get the string from the capture group NSString *string = [itemTitle substringWithRange:r]; // Replace occurrences of "Re: " NSString *replacementString = [string stringByReplacingOccurrencesOfString:@"Re: " withString:@""]; [i setTitle:replacementString];
Are we supposed to solve this challenge using regex instead? How do you negate the "Re: " phrase inside a capture group using regex? All I could manage was a general search for "Re: " and then used the NSRegularExpression instance method stringByReplacingMatchesInString:options:range:withTemplate: method applied to the previous regex:

NSRegularExpression *regReply = [[NSRegularExpression alloc] initWithPattern:@"Re: "
                                                                     options:0
                                                                       error:nil];
.
.
.
NSRange r = [result rangeAtIndex:1];
NSString *replacementString = [regReply stringByReplacingMatchesInString:[itemTitle substringWithRange:r]
                                                                 options:0
                                                                   range:NSMakeRange(0, 4)
                                                            withTemplate:@""];
[i setTitle:replacementString];

#2

@BryanLuby I, like you, solved this challenge in two different ways. And also like you, am not sure of the preferred way of solving this using regular expressions.
Both in the trimItemTitles method of RSSChannel.m:
Basic string manipulation way:

[code]if ([result numberOfRanges] == 2) {
// Pull out the second range, which will be the capture group (1 as zero-indexed)
NSRange r = [result rangeAtIndex:1];

// Silver Challenge - Processing the reply
NSString *postTitle = [itemTitle substringWithRange:r];
NSString *first4chars = [postTitle substringToIndex:4];
if ([first4chars isEqualToString:@"Re: "])
    postTitle = [postTitle substringFromIndex:4];
[i setTitle:postTitle];

}[/code]
Using regular expressions:

[code]if ([result numberOfRanges] == 2) {
// Pull out the second range, which will be the capture group (1 as zero-indexed)
NSRange r = [result rangeAtIndex:1];

// Silver Challenge - Processing the reply - regex
NSString *postTitle = [itemTitle substringWithRange:r];
NSRegularExpression *regReply = [[NSRegularExpression alloc] initWithPattern:@"Re: " options:0 error:nil];
NSArray *matchesReply = [regReply matchesInString:postTitle options:0 range:NSMakeRange(0, 4)];
if ([matchesReply count] > 0) {
    [i setTitle:[postTitle substringFromIndex:4]];
} else {
    [i setTitle:[itemTitle substringWithRange:r]];

}[/code]
I used NSMakeRange(0, 4) and substringFromIndex:4 as the "Re: " is always at the start of the post.

I prefer your way using stringByReplacingMatchesInString. Does anyone know if this is the best practice way?

Thanks, Mark


#3

I used NSRegularExpression because it was what we covered in this chapter. I added two lines of code to the trimItemTitles: method in RSSChannel to achieve the goal (see below).

[code]- (void)trimItemTitles
{
NSRegularExpression reg = [[NSRegularExpression alloc] initWithPattern:@". :: (.) :: ." options:0 error:nil];

for (RSSItem *i in items) {
    NSString *itemTitle = [i title];
    NSArray *matches = [reg matchesInString:itemTitle options:0 range:NSMakeRange(0, [itemTitle length])];
    
    if ([matches count] > 0) {
        NSTextCheckingResult *result = [matches objectAtIndex:0];

        if ([result numberOfRanges] == 2) {
            NSRange r = [result rangeAtIndex:1];
            [i setTitle:[itemTitle substringWithRange:r]];
            
            // I added these two lines
            NSRegularExpression *expressYourself = [NSRegularExpression regularExpressionWithPattern:@"Re: " options:0 error:nil];
            i.title = [expressYourself stringByReplacingMatchesInString:[i title] options:0 range:NSMakeRange(0, [[i title] length]) withTemplate:@""];
        }
    }
}

}[/code]


#4

Simpler solution: Prepend “(Re: )?” to the second parenthetical in the pattern, then update the range indices.

[code]- (void)trimItemTitles
{
NSRegularExpression reg = [[NSRegularExpression alloc] initWithPattern:@"(.) :: (Re: )?(.) :: ."
options:0
error:nil];

for (RSSItem *i in _items) {
	NSString *itemTitle = [i title];
	NSArray *matches = [reg matchesInString:itemTitle
									options:0
									  range:NSMakeRange(0, [itemTitle length])];
	
	if ([matches count] > 0) {
		NSTextCheckingResult *result = [matches objectAtIndex:0];
		NSRange r = [result range];
		NSLog(@"Match at (%d, %d) for %@!", r.location, r.length, itemTitle);
		
		if ([result numberOfRanges] == 4) {
			NSRange r = [result rangeAtIndex:1];
			[i setSubforum:[itemTitle substringWithRange:r]];
			
			r = [result rangeAtIndex:3];
			[i setTitle:[itemTitle substringWithRange:r]];
		}
	}
}

}[/code]


#5

Even simpler solution: use non-capturing parentheses (no need to update range indices).


#6

Wow, it is indeed simpler … if the silver challenge was all that was needed, I’d use it without hesitation.

But how then do you cope with the gold challenge ?
Because I guess you’d like to process the entries with a “Re :” separately, in order to have the replies point to the parent post, wouldn’t you ?

Just guessing, still working on the gold challenge …

Fred


#7

Hi,

I was using the solution posted by BananaSkin previously for this challenge, and then suddenly my program stopped working being on chapter 29 in mid chapter, took me a while to realize it was not the code of that current chapter, but actually the code to this solution from chapter 26…

The problem was that someone posted a 3 letter title… and this code tries to substring the first 4 characters of the title… causing it to crash… here is how I modified the code so it would not crash anymore…

[code]if ([result numberOfRanges] == 2) {
// Pull out the second range, which will be the capture group (1 as zero-indexed)
NSRange r = [result rangeAtIndex:1];

// Silver Challenge - Processing the reply
NSString *postTitle = [itemTitle substringWithRange:r];
NSRange re = [postTitle rangeOfString:@"Re: "];
// If "Re: " was found and it is located at the beginning of the second range, then do the substring
if (re.location == 0)
postTitle = [postTitle substringFromIndex:4];
[i setTitle:postTitle];

}[/code]


#8

Don’t know why but I didn’t pay attention to the great method stringByReplacingOccurrencesOfString, so I did it a bit another way:

				NSRange r = [result rangeAtIndex:2]; //getting the title
				NSString *checkString = [itemTitle substringWithRange:r];
				if ([checkString hasPrefix:@"Re: "]) {
					NSString *checkStringResults = [checkString substringFromIndex:4];
					[i setTitle:checkStringResults];
				}
				else {
					[i setTitle:[itemTitle substringWithRange:r]];
				}

#9

[quote=“alberto”]Even simpler solution: use non-capturing parentheses (no need to update range indices).

Thanks Alberto, this was really cool!