keyboard stuck when searching in uitableview using uisearchbar...? - iphone

I have a uitableview and uisearchbar. I'm binding table with tons of records and give functionality to user can search from that. But many times when data size is large keyboard stuck uptill it reload the table. Is it anyway so I can asynchronously search. It's iOS 4 app.
my uisearchbar textDidChange method is as below
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if(searchText.length == 0)
{
isFiltered = FALSE;
}
else
{
#autoreleasepool {
isFiltered = true;
membersMFilterArray = [[NSMutableArray alloc] init];
int lineCount = [[[membersMArray objectAtIndex:0] valueForKey:#"LineCount"] intValue];
NSString *membersMArrayValue;
for (int i=0; i<[membersMArray count]; i++)
{
membersMArrayValue = [membersMArray objectAtIndex:i];
NSString *line;
for (int j=0; j<lineCount; j++)
{
line = [NSString stringWithFormat:#"Line%d",j+1];
NSRange lineRange = [[membersMArrayValue valueForKey:line] rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(lineRange.location != NSNotFound)
{
[membersMFilterArray addObject:[membersMArray objectAtIndex:i]];
break;
}
}
}
}
}
[tblMember reloadData];}
my array is as below from which I've search, Actually this is one user data which arrise in one table row and if there is 10000 rows then multiply with this. So above for loop is I think as because of this way.
(
{
Line1 = "Ashish";
Line10 = "Ahmedabad";
Line11 = "Gujarat";
Line12 = "";
Line13 = "India";
Line14 = "";
Line15 = "";
Line16 = "abc#yahoo.com";
Line17 = "";
Line18 = "";
Line19 = "xyz";
Line2 = "Ahmedabad, Gujarat";
Line20 = "Jun 04, 2012";
Line3 = "";
Line4 = "";
Line5 = "";
Line6 = "abc";
Line7 = "xyz";
Line8 = "";
Line9 = "";
LineCount = 20;
"Member_id" = GM00018004;
RowNo = 01;
}
)

The short answer is yes. You can perform the search asynchronously, say, with Grand Central Dispatch. You can then notify the datasource of your table view to update.
However, it would be much better to make your search more efficient and achieve acceptable real time updates. How to achieve this, depends on how your data is stored and indexed. For example, if you are using Core Data you would have to tweak your NSFetchedResultsController and maybe construct a new entity with single strings of words occurring in your longer strings to be searched.
Looking at your search code, you have two nested loops - an almost certain recipe for performance degradation. Maybe you should consider some of Apple's automatic iteration mechanisms, (there are still iterations, but behind the scenes) such as with key paths or predicates. For instance, something like:
resultArray = [membersArray filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"self CONTAINS[cd] %#", searchText]];

From your code, i think the two for loops are unnecessary. Use better concept to sort the array items.
Some suggestions from my part to speedup.
Remove the auto-release-pool
Use one for-loop instead of two
You can check it out the start & end time of execution using NSlog.Discover the time taken process and fix it.
Example:
NSLog(#"loop1 started now")
for (int i=0; i<[membersMArray count]; i++)
{
membersMArrayValue = [membersMArray objectAtIndex:i];
NSString *line;
for (int j=0; j<lineCount; j++)
{
NSLog(#"loop2 started now")
line = [NSString stringWithFormat:#"Line%d",j+1];
NSRange lineRange = [[membersMArrayValue valueForKey:line] rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(lineRange.location != NSNotFound)
{
[membersMFilterArray addObject:[membersMArray objectAtIndex:i]];
break;
}
}
NSLog(#"loop2 ended now")
}
NSLog(#"loop1 ended now")

Related

Regex pattern and/or NSRegularExpression a bit too slow searching over very large file, can it be optimized?

In an iOS framework, I am searching through this 3.2 MB file for pronunciations: https://cmusphinx.svn.sourceforge.net/svnroot/cmusphinx/trunk/pocketsphinx/model/lm/en_US/cmu07a.dic
I am using NSRegularExpression to search for an arbitrary set of words that are given as an NSArray. The search is done through the contents of the large file as an NSString. I need to match any word that appears bracketed by a newline and a tab character, and then grab the whole line, for example if I have the word "monday" in my NSArray I want to match this line within the dictionary file:
monday M AH N D IY
This line starts with a newline, the string "monday" is followed by a tab character, and then the pronunciation follows. The entire line needs to be matched by the regex for its ultimate output. I also need to find alternate pronunciations of the words which are listed as follows:
monday(2) M AH N D EY
The alternative pronunciations always begin with (2) and can go as high as (5). So I also search for iterations of the word followed by parentheses containing a single number bracketed by a newline and a tab character.
I have a 100% working NSRegularExpression method as follows:
NSArray *array = [NSArray arrayWithObjects:#"friday",#"monday",#"saturday",#"sunday", #"thursday",#"tuesday",#"wednesday",nil]; // This array could contain any arbitrary words but they will always be in alphabetical order by the time they get here.
// Use this string to build up the pattern.
NSMutableString *mutablePatternString = [[NSMutableString alloc]initWithString:#"^("];
int firstRound = 0;
for(NSString *word in array) {
if(firstRound == 0) { // this is the first round
firstRound++;
} else { // After the first iteration we need an OR operator first.
[mutablePatternString appendString:[NSString stringWithFormat:#"|"]];
}
[mutablePatternString appendString:[NSString stringWithFormat:#"(%#(\\(.\\)|))",word]];
}
[mutablePatternString appendString:#")\\t.*$"];
// This results in this regex pattern:
// ^((change(\(.\)|))|(friday(\(.\)|))|(monday(\(.\)|))|(saturday(\(.\)|))|(sunday(\(.\)|))|(thursday(\(.\)|))|(tuesday(\(.\)|))|(wednesday(\(.\)|)))\t.*$
NSRegularExpression * regularExpression = [NSRegularExpression regularExpressionWithPattern:mutablePatternString
options:NSRegularExpressionAnchorsMatchLines
error:nil];
int rangeLocation = 0;
int rangeLength = [string length];
NSMutableArray * matches = [NSMutableArray array];
[regularExpression enumerateMatchesInString:string
options:0
range:NSMakeRange(rangeLocation, rangeLength)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop){
[matches addObject:[string substringWithRange:result.range]];
}];
[mutablePatternString release];
// matches array is returned to the caller.
My issue is that given the big text file, it isn't really fast enough on the iPhone. 8 words take 1.3 seconds on an iPhone 4, which is too long for the application. Given the following known factors:
• The 3.2 MB text file has the words to match listed in alphabetical order
• The array of arbitrary words to look up are always in alphabetical order when they get to this method
• Alternate pronunciations start with (2) in parens after the word, not (1)
• If there is no (2) there won't be a (3), (4) or more
• The presence of one alternative pronunciation is rare, occurring maybe 1 time in 8 on average. Further alternate pronunciations are even rarer.
Can this method be optimized, either by improving the regex or some aspect of the Objective-C? I'm assuming that NSRegularExpression is already optimized enough that it isn't going to be worthwhile trying to do it with a different Objective-C library or in C, but if I'm wrong here let me know. Otherwise, very grateful for any suggestions on improving the performance. I am hoping to make this generalized to any pronunciation file so I'm trying to stay away from solutions like calculating the alphabetical ranges ahead of time to do more constrained searches.
****EDIT****
Here are the timings on the iPhone 4 for all of the search-related answers given by August 16th 2012:
dasblinkenlight's create NSDictionary approach https://stackoverflow.com/a/11958852/119717: 5.259676 seconds
Ωmega's fastest regex at https://stackoverflow.com/a/11957535/119717: 0.609593 seconds
dasblinkenlight's multiple NSRegularExpression approach at https://stackoverflow.com/a/11969602/119717: 1.255130 seconds
my first hybrid approach at https://stackoverflow.com/a/11970549/119717: 0.372215 seconds
my second hybrid approach at https://stackoverflow.com/a/11970549/119717: 0.337549 seconds
The best time so far is the second version of my answer. I can't mark any of the answers best, since all of the search-related answers informed the approach that I took in my version so they are all very helpful and mine is just based on the others. I learned a lot and my method ended up a quarter of the original time so this was enormously helpful, thank you dasblinkenlight and Ωmega for talking it through with me.
Since you are putting the entire file into memory anyway, you might as well represent it as a structure that is easy to search:
Create a mutable NSDictionary words, with NSString keys and NSMutableArray values
Read the file into memory
Go through the string representing the file line-by-line
For each line, separate out the word part by searching for a '(' or a '\t' character
Get a sub-string for the word (from zero to the index of the '(' or '\t' minus one); this is your key.
Check if the words contains your key; if it does not, add new NSMutableArray
Add line to the NSMutableArray that you found/created at the specific key
Once your are finished, throw away the original string representing the file.
With this structure in hand, you should be able to do your searches in time that no regex engine would be able to match, because you replaced a full-text scan, which is linear, with a hash look-up, which is constant-time.
** EDIT: ** I checked the relative speed of this solution vs. regex, it is about 60 times faster on a simulator. This is not at all surprising, because the odds are stacked heavily against the regex-based solution.
Reading the file:
NSBundle *bdl = [NSBundle bundleWithIdentifier:#"com.poof-poof.TestAnim"];
NSString *path = [NSString stringWithFormat:#"%#/words_pron.dic", [bdl bundlePath]];
data = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
NSUInteger pos = 0;
NSMutableCharacterSet *terminator = [NSMutableCharacterSet characterSetWithCharactersInString:#"\t("];
while (pos != data.length) {
NSRange remaining = NSMakeRange(pos, data.length-pos);
NSRange next = [data
rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]
options:NSLiteralSearch
range:remaining
];
if (next.location != NSNotFound) {
next.length = next.location - pos;
next.location = pos;
} else {
next = remaining;
}
pos += (next.length+1);
NSString *line = [data substringWithRange:next];
NSRange keyRange = [line rangeOfCharacterFromSet:terminator];
keyRange.length = keyRange.location;
keyRange.location = 0;
NSString *key = [line substringWithRange:keyRange];
NSMutableArray *array = [tmp objectForKey:key];
if (!array) {
array = [NSMutableArray array];
[tmp setObject:array forKey:key];
}
[array addObject:line];
}
dict = tmp; // dict is your NSMutableDictionary ivar
Searching:
NSArray *keys = [NSArray arrayWithObjects:#"sunday", #"monday", #"tuesday", #"wednesday", #"thursday", #"friday", #"saturday", nil];
NSMutableArray *all = [NSMutableArray array];
NSLog(#"Starting...");
for (NSString *key in keys) {
for (NSString *s in [dict objectForKey:key]) {
[all addObject:s];
}
}
NSLog(#"Done! %u", all.count);
Try this one:
^(?:change|monday|tuesday|wednesday|thursday|friday|saturday|sunday)(?:\([2-5]\))?\t.*$
and also this one (using positive lookahead with list of possible first letters):
^(?=[cmtwfs])(?:change|monday|tuesday|wednesday|thursday|friday|saturday|sunday)(?:\([2-5]\))?\t.*$
and at the end, a version with some optimization:
^(?=[cmtwfs])(?:change|monday|t(?:uesday|hursday)|wednesday|friday|s(?:aturday|unday))(?:\([2-5]\))?\t.*$
Here is my hybrid approach of dasblinkenlight's and Ωmega's answers, which I thought I should add as an answer as well at this point. It uses dasblinkenlight's method of doing a forward search through the string and then performs the full regex on a small range in the event of a hit, so it exploits the fact that the dictionary and words to look up are both in alphabetical order and benefits from the optimized regex. Wish I had two best answer checks to give out! This gives the correct results and takes about half of the time of the pure regex approach on the Simulator (I have to test on the device later to see what the time comparison is on the iPhone 4 which is the reference device):
NSMutableArray *mutableArrayOfWordsToMatch = [[NSMutableArray alloc] initWithArray:array];
NSMutableArray *mutableArrayOfUnfoundWords = [[NSMutableArray alloc] init]; // I also need to know the unfound words.
NSUInteger pos = 0;
NSMutableString *mutablePatternString = [[NSMutableString alloc]initWithString:#"^(?:"];
int firstRound = 0;
for(NSString *word in array) {
if(firstRound == 0) { // this is the first round
firstRound++;
} else { // this is all later rounds
[mutablePatternString appendString:[NSString stringWithFormat:#"|"]];
}
[mutablePatternString appendString:[NSString stringWithFormat:#"%#",word]];
}
[mutablePatternString appendString:#")(?:\\([2-5]\\))?\t.*$"];
// This creates a string that reads "^(?:change|friday|model|monday|quidnunc|saturday|sunday|thursday|tuesday|wednesday)(?:\([2-5]\))?\t.*$"
// We don't want to instantiate the NSRegularExpression in the loop so let's use a pattern that matches everything we're interested in.
NSRegularExpression * regularExpression = [NSRegularExpression regularExpressionWithPattern:mutablePatternString
options:NSRegularExpressionAnchorsMatchLines
error:nil];
NSMutableArray * matches = [NSMutableArray array];
while (pos != data.length) {
if([mutableArrayOfWordsToMatch count] <= 0) { // If we're at the top of the loop without any more words, stop.
break;
}
NSRange remaining = NSMakeRange(pos, data.length-pos);
NSRange next = [data
rangeOfString:[NSString stringWithFormat:#"\n%#\t",[mutableArrayOfWordsToMatch objectAtIndex:0]]
options:NSLiteralSearch
range:remaining
]; // Just search for the first pronunciation.
if (next.location != NSNotFound) {
// If we find the first pronunciation, run the whole regex on a range of {position, 500} only.
int rangeLocation = next.location;
int searchPadding = 500;
int rangeLength = searchPadding;
if(data.length - next.location < searchPadding) { // Only use 500 if there is 500 more length in the data.
rangeLength = data.length - next.location;
}
[regularExpression enumerateMatchesInString:data
options:0
range:NSMakeRange(rangeLocation, rangeLength)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop){
[matches addObject:[data substringWithRange:result.range]];
}]; // Grab all the hits at once.
next.length = next.location - pos;
next.location = pos;
[mutableArrayOfWordsToMatch removeObjectAtIndex:0]; // Remove the word.
pos += (next.length+1);
} else { // No hits.
[mutableArrayOfUnfoundWords addObject:[mutableArrayOfWordsToMatch objectAtIndex:0]]; // Add to unfound words.
[mutableArrayOfWordsToMatch removeObjectAtIndex:0]; // Remove from the word list.
}
}
[mutablePatternString release];
[mutableArrayOfUnfoundWords release];
[mutableArrayOfWordsToMatch release];
// return matches to caller
EDIT: here is another version which uses no regex and shaves a little bit more time off of the method:
NSMutableArray *mutableArrayOfWordsToMatch = [[NSMutableArray alloc] initWithArray:array];
NSMutableArray *mutableArrayOfUnfoundWords = [[NSMutableArray alloc] init]; // I also need to know the unfound words.
NSUInteger pos = 0;
NSMutableArray * matches = [NSMutableArray array];
while (pos != data.length) {
if([mutableArrayOfWordsToMatch count] <= 0) { // If we're at the top of the loop without any more words, stop.
break;
}
NSRange remaining = NSMakeRange(pos, data.length-pos);
NSRange next = [data
rangeOfString:[NSString stringWithFormat:#"\n%#\t",[mutableArrayOfWordsToMatch objectAtIndex:0]]
options:NSLiteralSearch
range:remaining
]; // Just search for the first pronunciation.
if (next.location != NSNotFound) {
NSRange lineRange = [data lineRangeForRange:NSMakeRange(next.location+1, next.length)];
[matches addObject:[data substringWithRange:NSMakeRange(lineRange.location, lineRange.length-1)]]; // Grab the whole line of the hit.
int rangeLocation = next.location;
int rangeLength = 750;
if(data.length - next.location < rangeLength) { // Only use the searchPadding if there is that much room left in the string.
rangeLength = data.length - next.location;
}
rangeLength = rangeLength/5;
int newlocation = rangeLocation;
for(int i = 2;i < 6; i++) { // We really only need to do this from 2-5.
NSRange morematches = [data
rangeOfString:[NSString stringWithFormat:#"\n%#(%d",[mutableArrayOfWordsToMatch objectAtIndex:0],i]
options:NSLiteralSearch
range:NSMakeRange(newlocation, rangeLength)
];
if(morematches.location != NSNotFound) {
NSRange moreMatchesLineRange = [data lineRangeForRange:NSMakeRange(morematches.location+1, morematches.length)]; // Plus one because I don't actually want the line break at the beginning.
[matches addObject:[data substringWithRange:NSMakeRange(moreMatchesLineRange.location, moreMatchesLineRange.length-1)]]; // Minus one because I don't actually want the line break at the end.
newlocation = morematches.location;
} else {
break;
}
}
next.length = next.location - pos;
next.location = pos;
[mutableArrayOfWordsToMatch removeObjectAtIndex:0]; // Remove the word.
pos += (next.length+1);
} else { // No hits.
[mutableArrayOfUnfoundWords addObject:[mutableArrayOfWordsToMatch objectAtIndex:0]]; // Add to unfound words.
[mutableArrayOfWordsToMatch removeObjectAtIndex:0]; // Remove from the word list.
}
}
[mutableArrayOfUnfoundWords release];
[mutableArrayOfWordsToMatch release];
Looking at the dictionary file you provided, I'd say that a reasonable strategy could be reading in the data and putting it into any sort of persistent data store.
Read through the file and create objects for each unique word, with n strings of pronunciations (where n is the number of unique pronunciations). The dictionary is already in alphabetical order, so if you parsed it in the order that you're reading it you'd end up with an alphabetical list.
Then you can do a binary search on the data - even with a HUGE number of objects a binary search will find what you're looking for very quickly (assuming alphabetical order).
You could probably even keep the whole thing in memory if you need lightning-fast performance.

Filtering an NSArray from JSON?

I'm trying to implement a searchable tableview in my app, where when someone can search a location and get results. It looks something like this:
I'm getting my source from genomes.com which gives more then just cities, it also has parks, buildings, counties, etc. I want to just show locations which are cities.
The data is a JSON file which is parsed by JSONKit. The whole file comes in (maximum 20 objects) and then the searchable table view shows it. I'm not sure if I should parse the JSON file differently, or if I should make the table view show only the results needed. (Performance in this case is not an issue.). The JSON file gets converted to an NSArray.
Here is part of the array:
{
adminCode1 = MA;
adminCode2 = 027;
adminName1 = Massachusetts;
adminName2 = "Worcester County";
adminName3 = "";
adminName4 = "";
adminName5 = "";
continentCode = NA;
countryCode = US;
countryName = "United States";
elevation = 178;
fcl = A;
fclName = "country, state, region,...";
fcode = ADMD;
fcodeName = "administrative division";
geonameId = 4929431;
lat = "42.2000939";
lng = "-71.8495163";
name = "Town of Auburn";
population = 0;
score = "53.40083694458008";
timezone = {
dstOffset = "-4";
gmtOffset = "-5";
timeZoneId = "America/New_York";
};
toponymName = "Town of Auburn";
},
What I want to do is if the "fcl" (seen in the array) is equal to P, then I want it to show that in the table view. If the "fcl" is some other character, then I don't want it to be seen in the table view. I'm pretty sure that an if statement can do that, but I don't know how to get it so that it filters part of it.
Any help would be appreciated! Thanks
EDIT: As of now, this is the code to search:
- (void)delayedSearch:(NSString*)searchString
{
[self.geoNamesSearch cancel];
[self.geoNamesSearch search:searchString
maxRows:20
startRow:0
language:nil];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
self.searchDisplayController.searchBar.prompt = NSLocalizedStringFromTable(#"ILGEONAMES_SEARCHING", #"ILGeoNames", #"");
[self.searchResults removeAllObjects];
// Delay the search 1 second to minimize outstanding requests
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(delayedSearch:) withObject:searchString afterDelay:0];
return YES;
}
Your question is basically, how do you filter your array from a search bar string? If so, you can detect when the text changes via UISearchBarDelegate and then go through your array copying those objects that contain the string you are looking for, i.e.
This is the delegate method you want: searchBar:textDidChange:.
[filterArray removeAllObjects];
for(int i = 0; i < [normalArray count]; i++){
NSRange textRange;
textRange =[[[[normalArray objectAtIndex:i] objectForKey:#"name"] lowercaseString] rangeOfString:[searchBarString lowercaseString]];
//I wasn't sure which objectForKey: string you were looking for, just replace the one you want to filter.
if(textRange.location != NSNotFound)
{
[filterArray addObject:[normalArray objectAtIndex:i]];
}
}
filterTableView = YES;
[tableView reloadData];
Note the filterTableView bool value, this is so your tableView knows either to load normally or the filtered version you just made. You implement this in:
tableView:numberOfRowsInSection: //For number of rows.
tableView:cellForRowAtIndexPath: //For the content of the cells.
Hope this is what you were looking for.
NSMutableArray* filtered = [[NSMutableArray alloc] autorelease];
for (int i=0;i<[data count];i++)
{
NSDictionary* item=[data objectAtIndex:i];
if (#"P" == [item objectForKey:#"fcl"] )
{
[filtered addObject:item];
}
}
So every time the search field changes, you will compute a new array, and then reload your tableview. The number of rows will be the numbers of rows in your filtered array.
To compute the new array, you can do this (assuming an array of dictionaries):
NSString *searchString; // from the search field
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[origArray count]];
for(NSDictionary *dict in origArray) {
NSString *val = [dict objectForKey:#"fcl"];
if([val length] >= searchString) {
NSString subString = [val substringToIndex:[searchString length]];
if([subString isEqualToString:val]) [array addObject:dict];
}
}
Each cell then will get its values from the new array.
Just put your json in a NSDictionary and simply do something like :
if ([[yourJson objectForKey:#"fcl"] stringValue] == #"A")
//doSomething

iOS Search Results Order

I have a UISearchBar that searches a table of data. When a search is typed in, the results are displayed in an order like so:
Starts typing 'ch...'
1. Ache
2. Cherries
3. Choice
It makes more sense to me (and for my app) that 'Cherries' and 'Choice' would be at the top of the results, because 'ch' and not 'ach' was typed in. Is this something that can be changed programatically, or is it just the way iOS searches work?
You have to give the search option as NSAnchoredSearch
NSRange searchRange = [sortedString rangeOfString:searchText options:NSAnchoredSearch];
Some of the search method listed below
NSCaseInsensitiveSearch
NSLiteralSearch
NSBackwardsSearch
NSAnchoredSearch
NSNumericSearch
NSDiacriticInsensitiveSearch
NSWidthInsensitiveSearch
NSForcedOrderingSearch
NSRegularExpressionSearch
Eg:
- (void)search {
NSString *searchText = [searchBar.text lowercaseString];
for (int index = 0; index < [availableCollectionArray count]; index++) {
NSArray *tempArray = [availableCollectionArray objectAtIndex:index];
for (int tempIndex = 0; tempIndex < [tempArray count] ; tempIndex++) {
NSString *sortedString = [tempArray objectAtIndex:tempIndex];
NSRange searchRange = [sortedString rangeOfString:searchText options:NSAnchoredSearch];
if (searchRange.length > 0)
{
[sortedArray addObject: sortedString]; //add the string which starts from searchBar.text
}
}
}
}

Better way to handle arrays in Objective-C?

Please let me preface this question with an apology. I am very new to Objective C. Unfortunately, I have a very tight timeline on a project for work and need to get up to speed as fast as possible.
The code below works. I am just wondering if there is a better way to handle this... Maybe something more Cocoa-ish. The entire purpose of the function is to get an ordered list of positions in a string that have a certain value.
I fell back on a standard array for comfort reasons. The NSMutableString that I initially declare is only for testing purposes. Being a complete noob to this language and still wrapping my head around the way Objective C (and C++ I guess) handles variables and pointers, any and all hints, tips, pointers would be appreciated.
NSMutableString *mut = [[NSMutableString alloc] initWithString:#"This is a test of the emergency broadcast system. This is only a test."];
NSMutableArray *tList = [NSMutableArray arrayWithArray:[mut componentsSeparatedByString:#" "]];
int dump[(tList.count-1)];
int dpCount=0;
NSUInteger length = [mut length];
NSRange myRange = NSMakeRange(0, length);
while (myRange.location != NSNotFound) {
myRange = [mut rangeOfString:#" " options: 0 range:myRange];
if (myRange.location != NSNotFound) {
dump[dpCount]=myRange.location;
++dpCount;
myRange = NSMakeRange((myRange.location+myRange.length), (length - (myRange.location+myRange.length)));
}
}
for (int i=0; i < dpCount; ++i) {
//Going to do something with these values in the future... they MUST be in order.
NSLog(#"Dumping positions in order: %i",dump[i]);
}
text2.text = mut;
[mut release];
Thanks again for any replies.
There is not great way to do what you are trying to do. Here is one way:
// locations will be an NSArray of NSNumbers --
// each NSNumber containing one location of the substring):
NSMutableArray *locations = [NSMutableArray new];
NSRange searchRange = NSMakeRange(0,string.length);
NSRange foundRange;
while (searchRange.location < string.length) {
searchRange.length = string.length-searchRange.location;
foundRange = [string rangeOfString:substring options:nil range:searchRange];
if(foundRange.location == NSNotFound) break;
[locations addObject:[NSNumber numberWithInt:searchRange.location]];
searchRange.location = foundRange.location+foundRange.length;
}
This might be a little more streamlined (and faster in terms of execution time, if that matters):
const char *p = "This is a test of the emergency broadcast system. This is only a test.";
NSMutableArray *positions = [NSMutableArray array];
for (int i = 0; *p; p++, i++)
{
if (*p == ' ') {
[positions addObject:[NSNumber numberWithInt:i]];
}
}
for (int j = 0; j < [positions count]; j++) {
NSLog(#"position %d: %#", j + 1, [positions objectAtIndex:j]);
}

stringByReplacingCharactersInRange don't replace It's Append !

I Spent 5 hours try to figure a way for that..i'm trying to do a hangman app for iphone and the method below is the method that should be called when the player chooses a character and it match the chosen word..
-(void)replaceTheHiddenTextWithNewText:(NSString*)character{
NSString *fullTextField = fullText.text;
int textCount = [hiddenText.text length];
NSString *theRiddle;
for (int i = textCount-1 ; i>=0; i--) {
NSString *hiddenTextField = [[NSMutableString alloc] initWithString:hiddenText.text];
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i/3,1)];
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}else{
theRiddle = [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#"_"];
}
hiddenTextField = theRiddle;
}
hiddenText.text=theRiddle;
}
the problem is stringByReplacingCharactersInRange doesn't replace the character, it appends it to the underscore what am I doing wrong here?
Best Regards,
M Hegab
Just played around with your code. It does not work, but stringByReplacingCharactersInRange is not your problem.
Your game logic doesn't work like it should. Get a pen and a sheet of paper and "manually" loop through your for loop to see that this must be wrong.
Next time, if you've stared at code for half an hour, take a pen. This will save you at least 4 hours :-)
There are some issues with your code. Assume Kartoffelkäfer is the word you are looking for, and the user enters the letter f.
for (int i = textCount-1 ; i>=0; i--) {
NSString *hiddenTextField = [[NSMutableString alloc] initWithString:hiddenText.text];
// you are creating this string in every loop from the text of a (I guess) UITextField.
// I don't know what the content of this text is but I guess it is suppossed to be `______________`
// in every loop you replace the word where you replaced the _ with the correct letter with the string from the textfield.
// Btw, you are leaking this string.
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i/3,1)];
// Kartoffelkäfer has 14 chars so i is 13. And 13/3 is 4. And the character at index 4 is o
// In the next loop i is 12. And 12/3 is 4, too.
// next three loops will give you index 3. Then you get three times index 2, and so one.
// you never reach the letter f, anyway.
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}else{
theRiddle = [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#"_"];
// You should not replace a unmatched character with a _ . Because already matched letters would be overwritten.
}
hiddenTextField = theRiddle;
}
I assumed that the content of hiddenText.text is #"______"
and the content of fullText.text is #"Kartoffelkäfer". So hiddentext is the exact length as the fullText.
What I had to change to get this to work:
NSString *theRiddle;
NSString *hiddenTextField = [[[NSMutableString alloc] initWithString:hiddenText.text] autorelease];
for (int i = textCount-1 ; i>=0; i--) {
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i,1)];
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}
else {
theRiddle = hiddenTextField;
}
hiddenTextField = theRiddle;
}
hiddenText.text=theRiddle;
Far away from good code, but I tried to change your code as little as possible.