search NSArray with regex - objective-c

I have an array of names. If any of the name is already there then on inserting new name I want to append the counter eg John (02) if John already present in array then John (03) if it is third entry of name John.
Is there any way to filter array with Regex so that I can filter all records with pattern "John (xx)"?

Yup. You have to loop through the array and check with regex. You have to do this, since if you just check if the array contains your string, it won't return true if you search for "John" and the only one in your array is "John1"
NSMutableArray *testArray = [[NSMutableArray alloc] initWithObjects:#"John", #"Steve", #"Alan", #"Brad", nil];
NSString *nameToAdd = #"John";
NSString *regex = [NSString stringWithFormat:#"%#[,]*[0-9]*", nameToAdd];
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
for (int i = 0; i < [testArray count]; i++)
{
NSString *string = [testArray objectAtIndex:i];
if ([myTest evaluateWithObject:string])
{
// Matches
NSLog(#" match !");
int currentValue;
NSArray *split = [string componentsSeparatedByString:#","];
if ([split count] == 1)
{
// Set to 2
currentValue = 2;
}
else
{
currentValue = [[split objectAtIndex:1] intValue];
currentValue++;
}
NSString *newString = [NSString stringWithFormat:#"%#,%d", nameToAdd, currentValue];
[testArray replaceObjectAtIndex:i withObject:newString];
}
}
for (NSString *string in testArray)
{
NSLog(#"%#", string);
}
This will replace "John" with "John,2", and if you search for "John" a third time it will replace it with "John,3".
Hope this helps

You can create a predicate for your regular expression and then filter the array using the predicate. Based on the count of the matches, you can update the new value being added as needed.
NSMutableArray *currentNames = ... // the current list of names
NSString *newName = ... // the new name to add
NSString *regex = [NSString stringWithFormat:#"%# \\([0-9]*\\)", newName];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
NSArray *matches = [currentNames filteredArrayUsingPredicate:filter];
if (matches.count) {
NSString *updatedName = [NSString stringWithFormat:#"%# (%02d)", newName, matches.count];
[currentNames addObject:updatedName];
} else {
[currentNames addObject:newName];
}

You can filer an array with NSPredicate. Not too familiar with regex, but the following seems okay:
NSArray *array = #[#"John (01)", #"John (02)", #"John XX"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES 'John [(]\\\\d{2}[)]'"];
NSArray *result = [array filteredArrayUsingPredicate:predicate];
NSLog( #"%#", result ); // Output John (01), John (02)

Related

NSPredicate to match string with an array of prefixes

I'm unsure how to write this NSPredicate to achieve the following. I have an array of prefixes, and I want to know if any of them (plus an underscore) are the prefix of a given string. I don't need to know which matched, just a yes/no if any matched at all.
I can't seem to work this out, at the moment I have this
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSArray *bins = #[#"aaa", #"bbb", #"ccc"];
NSString* item = #"aaa_blah";
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%# BEGINSWITH SELF", item];
NSLog(#"%#", [[bins filteredArrayUsingPredicate:pred] count] ? #"YES" : #"NO");
}
}
The only way I could think of doing it was filtering the array - so firstly is there a better approach?
And secondly, I want it to return true only if the prefix is followed by an underscore so
#[#"aaa", #"bbb", #"ccc"];
#"aaa_blah"; // YES
#"aaablah"; // NO
#"bbbblah"; // NO
I'm not sure how to do that?
+(void)checkIfExists:(NSArray *)prefixes inMyobjects:(NSArray *)myObjects withDivider:(NSString *)divider
{
divider = #"_";
prefixes = #[#"aaa",#"bbb",#"ccc"];
myObjects = #[#"aaa_sd",#"dsf_ds",#"aaa_sss",#"aaabbb"];
NSMutableArray * resultsOfPredicate = [NSMutableArray new];
for (NSString * pre in prefixes)
{
NSString * iAmLookingFor = [NSString stringWithFormat:#"%#%#", pre, divider];
NSPredicate *prefixPredicate = [NSPredicate predicateWithFormat:#"SELF beginsWith[c] %#", iAmLookingFor];
NSArray * resultOfSearch = [myObjects copy];
resultOfSearch = [resultOfSearch filteredArrayUsingPredicate:prefixPredicate];
NSLog(#"ros %#",resultOfSearch);
[resultsOfPredicate addObject:#([resultOfSearch count])];
}
for (int i = 0; i<[resultsOfPredicate count]; i++)
{
NSLog(#"prefix %# isAppeared:%d",[prefixes objectAtIndex:i], [[resultsOfPredicate objectAtIndex:i] boolValue]);
}
}
I hope this will help.

Objective-C: How to find the most common string in an array?

I have an array of strings from an online database that I trying to determine the most commonly used word. The values inside the arrays will vary but I want to check the most common words of whatever collection or words I'm using. If theoretically I had an array of the following...
NSArray *stringArray = [NSArray arrayWithObjects:#"Duck", #"Duck", #"Duck", #"Duck", #"Goose"];
How do I iterate through this array to determine the most common string, which would obviously be "Duck"?
Simplest way is probably NSCountedSet:
NSCountedSet* stringSet = [[NSCountedSet alloc] initWithArray:strings];
NSString* mostCommon = nil;
NSUInteger highestCount = 0;
for(NSString* string in stringSet) {
NSUInteger count = [stringSet countForObject:string];
if(count > highestCount) {
highestCount = count;
mostCommon = string;
}
}
You can use the word as a key into a dictionary.
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
}
Now iterate through words and find the key with the highest value.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
for (NSString *key in [words allKeys]) {
if ([words[key] compare:curMax] == NSOrderedDescending) {
mostCommon = key;
curMax = word[key];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
EDIT: Rather than looping through the array once then looping separately through the sorted dictionary, I think we can do better and do it all in a single loop.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
if ([words[word] compare:curMax] == NSOrderedDescending) {
mostCommon = word;
curMax = words[word];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
This should be significantly faster than my answer pre-edit, though I don't know how it compares to using the NSCountedSet answer.
Try using NSPredicate.
NSUInteger count=0;
NSString *mostCommonStr;
for(NSString *strValue in stringArray) {
NSUInteger countStr=[[stringArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self MATCHES[CD] %#, strValue]]count];
if(countStr > count) {
count=countStr;
mostCommonStr=strValue;
}
}
NSLog(#"The most commonstr is %#",mostCommonStr);

obj c -get list of indexes in NSArray from NSPredicate

I have an array of car and I am filtering it based on objects containing the letter i.
NSMutableArray *cars = [NSMutableArray arrayWithObjects:#"Maruthi",#"Hyundai", #"Ford", #"Benz", #"BMW",#"Toyota",nil];
NSString *stringToSearch = #"i";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",stringToSearch]; // if you need case sensitive search avoid '[c]' in the predicate
NSArray *results = [cars filteredArrayUsingPredicate:predicate];
results contains Maruthi,Hyundai. Instead of the elements, I want results to contain the indexes of the elements i.e 0,1.
NSMutableArray *cars = [NSMutableArray arrayWithObjects:#"Maruthi",#"BMW", #"Ford", #"Benz", #"Hyundai",#"Toyota",nil];
NSMutableArray * results = [[NSMutableArray alloc]init];
for(int i = 0;i<cars.count;i++)
{
NSString * obj = [cars objectAtIndex:i];
if([obj rangeOfString:#"i"].location == NSNotFound)
{
NSLog(#"Not Found");
}
else
{
int index = [cars indexOfObject:obj];
[results addObject:[NSNumber numberWithInt:index]];
}
}
Why not use
- (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
Or similar?
Depending on your search criteria, something like this perhaps?
NSArray *array = #[#"Maruthi",#"Hyundai", #"Ford", #"Benz", #"BMW", #"Toyota"];
NSString *stringToSearch = #"i";
NSIndexSet *set = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSString *string = obj;
if (([string rangeOfString:stringToSearch].location == NSNotFound))
{
return NO;
}
return YES;
}];

how to match string portion with NSArray values in objective-c

I have following array and search string.
NSArray *values =[NSArray arrayWithObjects:#"abc",#"xyz",#"cba",#"yzx",nil];
NSString *search = #"startcba";
I want to search string's end part within an array elements. My expected search result will be #"cba". Please let me know how to find the desire value in array for giving search.
Thanks,
You can use NSPredicate to get the elements that satisfy your requirement.
NSPredicate * predicate = [NSPredicate predicateWithFormat:#" %# ENDSWITH SELF ", search];
NSArray * searchResults = [values filteredArrayUsingPredicate:predicate];
The NSPredicates way is great.
Here is an approach with rangeOfString:
NSArray *values =[NSArray arrayWithObjects:#"abc",#"xyz",#"cba",#"yzx",nil];
NSString *search = #"startcba";
NSUInteger searchLength = [search length];
NSString *result = nil;
for (NSString *val in values)
{
NSUInteger valLength = [val length];
NSRange expectedRange = NSMakeRange(searchLength - valLength, valLength);
NSRange rng = [search rangeOfString:val];
if ( rng.location == expectedRange.location && rng.length == expectedRange.length )
{
result = val;
break;
}
}

How can I optimise out this nested for loop?

How can I optimise out this nested for loop?
The program should go through each word in the array created from the word text file, and if it's greater than 8 characters, add it to the goodWords array. But the caveat is that I only want the root word to be in the goodWords array, for example:
If greet is added to the array, I don't want greets or greetings or greeters, etc.
NSString *string = [NSString stringWithContentsOfFile:#"/Users/james/dev/WordParser/word.txt" encoding:NSUTF8StringEncoding error:NULL];
NSArray *words = [string componentsSeparatedByString:#"\r\n"];
NSMutableArray *goodWords = [NSMutableArray array];
BOOL shouldAddToGoodWords = YES;
for (NSString *word in words)
{
NSLog(#"Word: %#", word);
if ([word length] > 8)
{
NSLog(#"Word is greater than 8");
for (NSString *existingWord in [goodWords reverseObjectEnumerator])
{
NSLog(#"Existing Word: %#", existingWord);
if ([word rangeOfString:existingWord].location != NSNotFound)
{
NSLog(#"Not adding...");
shouldAddToGoodWords = NO;
break;
}
}
if (shouldAddToGoodWords)
{
NSLog(#"Adding word: %#", word);
[goodWords addObject:word];
}
}
shouldAddToGoodWords = YES;
}
How about something like this?
//load the words from wherever
NSString * allWords = [NSString stringWithContentsOfFile:#"/usr/share/dict/words"];
//create a mutable array of the words
NSMutableArray * words = [[allWords componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy];
//remove any words that are shorter than 8 characters
[words filterUsingPredicate:[NSPredicate predicateWithFormat:#"length >= 8"]];
//sort the words in ascending order
[words sortUsingSelector:#selector(caseInsensitiveCompare:)];
//create a set of indexes (these will be the non-root words)
NSMutableIndexSet * badIndexes = [NSMutableIndexSet indexSet];
//remember our current root word
NSString * currentRoot = nil;
NSUInteger count = [words count];
//loop through the words
for (NSUInteger i = 0; i < count; ++i) {
NSString * word = [words objectAtIndex:i];
if (currentRoot == nil) {
//base case
currentRoot = word;
} else if ([word hasPrefix:currentRoot]) {
//word is a non-root word. remember this index to remove it later
[badIndexes addIndex:i];
} else {
//no match. this word is our new root
currentRoot = word;
}
}
//remove the non-root words
[words removeObjectsAtIndexes:badIndexes];
NSLog(#"%#", words);
[words release];
This runs very very quickly on my machine (2.8GHz MBP).
A Trie seems suitable for your purpose. It is like a hash, and is useful for detecting if a given string is a prefix of an already seen string.
I used an NSSet to ensure that you only have 1 copy of a word added at a time. It will add a word if the NSSet does not already contain it. It then checks to see if the new word is a substring for any word that has already been added, if true then it won't add the new word. It's case-insensitive as well.
What I've written is a refactoring of your code. It's probably not that much faster but you really do want a tree data structure if you want to make it a lot faster when you want to search for words that have already been added to your tree.
Take a look at RedBlack Trees or B-Trees.
Words.txt
objective
objectively
cappucin
cappucino
cappucine
programme
programmer
programmatic
programmatically
Source Code
- (void)addRootWords {
NSString *textFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"txt"];
NSString *string = [NSString stringWithContentsOfFile:textFile encoding:NSUTF8StringEncoding error:NULL];
NSArray *wordFile = [string componentsSeparatedByString:#"\n"];
NSMutableSet *goodWords = [[NSMutableSet alloc] init];
for (NSString *newWord in wordFile)
{
NSLog(#"Word: %#", newWord);
if ([newWord length] > 8)
{
NSLog(#"Word '%#' contains 8 or more characters", newWord);
BOOL shouldAddWord = NO;
if ( [goodWords containsObject:newWord] == NO) {
shouldAddWord = YES;
}
for (NSString *existingWord in goodWords)
{
NSRange textRange = [[newWord lowercaseString] rangeOfString:[existingWord lowercaseString]];
if( textRange.location != NSNotFound ) {
// newWord contains the a substring of existingWord
shouldAddWord = NO;
break;
}
NSLog(#"(word:%#) does not contain (substring:%#)", newWord, existingWord);
shouldAddWord = YES;
}
if (shouldAddWord) {
NSLog(#"Adding word: %#", newWord);
[goodWords addObject:newWord];
}
}
}
NSLog(#"***Added words***");
int count = 1;
for (NSString *word in goodWords) {
NSLog(#"%d: %#", count, word);
count++;
}
[goodWords release];
}
Output:
***Added words***
1: cappucino
2: programme
3: objective
4: programmatic
5: cappucine