Checking if NSMutableArray of names has prefix of multiple characters - ios

I'm trying to split contact names into separate NSMutableArrays and want to include letters with accents amongst these arrays.
if([[contact.givenName lowercaseString] hasPrefix:#"a"]){
[selectedArray addObject:contact];
NSLog(#"%#",contact.givenName);
}
The above works fine for the letter "a", but I wish to include the following:
#"a",#"á",#"â",#"ã",#"ä"
What is the correct way to do this?
Thanks

As Larme suggested you need to use NSDiacriticInsensitiveSearch like this.
if ([contact.givenName length] >= 1) {
NSString *nameFirstLetter = [contact.givenName substringToIndex:1];
NSStringCompareOptions options = NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch;
if([nameFirstLetter compare:#"a" options:options] == NSOrderedSame){
[selectedArray addObject:contact];
NSLog(#"%#",contact.givenName);
}
}

You can filter the array with a predicate, [cd] means case and diacritic insensitive.
Assuming contacts is the array containing the contact instances
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"givenName BEGINSWITH[cd] %#", #"a"];
NSArray *selectedArray = [contacts filteredArrayUsingPredicate:predicate];
The solution is very fast, you even don't need a repeat loop and it handles also the case if the property is empty or nil.

You need to decode your string before check :
NSString *str = #"ä";
NSData *data = [str dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *newStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
if([[newStr lowercaseString] hasPrefix:#"a"]){
NSLog(#"%#",newStr);
}

Use NSDiacriticInsensitiveSearch
NSString *string =#"á";
if ([string rangeOfString:#"a" options:NSDiacriticInsensitiveSearch].location
!= NSNotFound)
{
NSLog(#"found words");
}

Related

Check that string contains a character from set of characters

I need to check the complexity of a password. One of the conditions is that the password must contain at least one number. I've tried the following approach but it does not give me expected results and I don't know what's wrong.
NSString *regexpNumbers = #"[0-9]+";
NSPredicate *predicateNumbers = [NSPredicate predicateWithFormat:#"SELF CONTAINS %#", regexpNumbers];
result &= [predicateNumbers evaluateWithObject:password];
evaluateWithObject: method returns NO even if the password contains some number.
Using rangeOfCharacterFromSet:
You can create your own character set like the way:
NSCharacterSet *s = [NSCharacterSet characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"];
s = [s invertedSet];
NSString *string = #"String to find";
NSRange r = [string rangeOfCharacterFromSet:s];
if (r.location != NSNotFound) {
NSLog(#"the string contains illegal characters");
} else {
NSLog(#"Found!!!");
}
Using NSPredicate:
NSString *myRegex = #"[A-Z0-9a-z_]*";
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", myRegex];
NSString *string = #"String to find";
BOOL valid = [myTest evaluateWithObject:string];
When using NSPredicate with regex you need to use MATCHES, not CONTAINS (which is used for direct literal comparison).
Arguably you should't be doing this though. There is an argument that it should be done on the server.
Instead of using regex you could also look to use rangeOfCharacterFromSet: and NSCharacterSet for each of your checks.

Split NSString from first whitespace

I have a name textfield in my app, where both the firstname maybe a middle and a lastname is written. Now I want to split these components by the first whitespace, the space between the firstname and the middlename/lastname, so I can put it into my model.
For example:
Textfield Text: John D. Sowers
String 1: John
String 2: D. Sowers.
I have tried using [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] firstObject]; & [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lastObject];
But these only work if have a name without a middlename. Since it gets the first and the last object, and the middlename is ignored.
So how would I manage to accomplish what I want?
/*fullNameString is an NSString*/
NSRange rangeOfSpace = [fullNameString rangeOfString:#" "];
NSString *first = rangeOfSpace.location == NSNotFound ? fullNameString : [fullNameString substringToIndex:rangeOfSpace.location];
NSString *last = rangeOfSpace.location == NSNotFound ? nil :[fullNameString substringFromIndex:rangeOfSpace.location + 1];
...the conditional assignment (rangeOfSpace.location == NSNotFound ? <<default value>> : <<real first/last name>>) protects against an index out of bounds error.
Well that method is giving you an array with all the words split by white space, so then you can grab the first object as the first name and the rest of the objects as middle/last/etc
NSArray *ar = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *firstName = [ar firstObject];
NSMutableString *rest = [[NSMutableString alloc] init];
for(int i = 1; i < ar.count; i++)
{
[rest appendString:[ar objectAtIndex:i]];
[rest appendString:#" "];
}
//now first name has the first name
//rest has the rest
There might be easier way to do this, but this is one way..
Hope it helps
Daniel
I think this example below I did, solves your problem.
Remember you can assign values from the array directly, without transforming into string.
Here is an example:
NSString *textField = #"John D. Sowers";
NSArray *fullName = [textField componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
if (fullName.count)
{
if (fullName.count > 2)
{
NSLog(#"Array has more than 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *middleName = fullName[1];
NSLog(#"Middle Name:%#",middleName);
NSString *lastName = fullName[2];
NSLog(#"Last Name:%#",lastName);
}
else if(fullName.count == 2)
{
NSLog(#"Array has 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *lastName = fullName[1];
NSLog(#"Last Name:%#",lastName);
}
else
{
NSString *name = fullName[0];
}
}
I found this to be most robust:
NSString *fullNameString = #"\n Barnaby Marmaduke \n \n Aloysius ";
NSMutableArray *nameArray = [[fullNameString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
[nameArray removeObject:#""];
NSString *firstName = [nameArray firstObject];
if(nameArray.count)
{
[nameArray removeObjectAtIndex:0];
}
NSString *nameRemainder = [nameArray componentsJoinedByString:#" "];
Bob's your uncle.

Regex in objective C

I want to extract only the names from the following string
bob!33#localhost #clement!17#localhost jack!03#localhost
and create an array [#"bob", #"clement", #"jack"].
I have tried NSString's componentsseparatedbystring: but it didn't work as expected. So I am planning to go for regEx.
How can I extract strings between ranges and add it to an array
using regEx in objective C?
The initial string might contain more than 500 names, would it be a
performance issue if I manipulate the string using regEx?
You can do it without regex as below (Assuming ! sign have uniform pattern in your all words),
NSString *names = #"bob!33#localhost #clement!17#localhost jack!03#localhost";
NSArray *namesarray = [names componentsSeparatedByString:#" "];
NSMutableArray *desiredArray = [[NSMutableArray alloc] initWithCapacity:0];
[namesarray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSRange rangeofsign = [(NSString*)obj rangeOfString:#"!"];
NSString *extractedName = [(NSString*)obj substringToIndex:rangeofsign.location];
[desiredArray addObject:extractedName];
}];
NSLog(#"%#",desiredArray);
output of above NSLog would be
(
bob,
"#clement",
jack
)
If you still want to get rid of # symbol in above string you can always replace special characters in any string, for that check this
If you need further help, you can always leave comment
NSMutableArray* nameArray = [[NSMutableArray alloc] init];
NSArray* youarArray = [yourString componentsSeparatedByString:#" "];
for(NSString * nString in youarArray) {
NSArray* splitObj = [nString componentsSeparatedByString:#"!"];
[nameArray addObject:[splitObj[0]]];
}
NSLog(#"%#", nameArray);
I saw the other solutions and it seemed no one tried to use real regular expressions here, so I created a solution which uses it, maybe you or someone else can use it as a possible idea in the future:
NSString *_names = #"bob!33#localhost #clement!17#localhost jack!03#localhost";
NSError *_error;
NSRegularExpression *_regExp = [NSRegularExpression regularExpressionWithPattern:#" ?#?(.*?)!\\d{2}#localhost" options:NSRegularExpressionCaseInsensitive error:&_error];
NSMutableArray *_namesOnly = [NSMutableArray array];
if (!_error) {
NSLock *_lock = [[NSLock alloc] init];
[_regExp enumerateMatchesInString:_names options:NSMatchingReportProgress range:NSMakeRange(0, _names.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
if (result.numberOfRanges > 1) {
if ([_lock tryLock]) [_namesOnly addObject:[_names substringWithRange:[result rangeAtIndex:1]]], [_lock unlock];
}
}];
} else {
NSLog(#"error : %#", _error);
}
the result can be logged...
NSLog(#"_namesOnly : %#", _namesOnly);
...and that will be:
_namesOnly : (
bob,
clement,
jack
)
Or even something as simple as this will do the trick:
NSString *strNames = #"bob!33#localhost #clement!17#localhost jack!03#localhost";
strNames = [[strNames componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]]
componentsJoinedByString:#""];
NSArray *arrNames = [strNames componentsSeparatedByString:#"localhost"];
NSLog(#"%#", arrNames);
Output:
(
bob,
clement,
jack,
""
)
NOTE: Ignore the last element index while iterating or whatever
Assumption:
"localhost" always comes between names
I know it ain't so optimized but it's one way to do this

NSSet to string separaing by comma

I have the next code for converting NSSet to string separating by comma:
-(NSString *)toStringSeparatingByComma
{
NSMutableString *resultString = [NSMutableString new];
NSEnumerator *enumerator = [self objectEnumerator];
NSString* value;
while ((value = [enumerator nextObject])) {
[resultString appendFormat:[NSString stringWithFormat:#" %# ,",value]];//1
}
NSRange lastComma = [resultString rangeOfString:#"," options:NSBackwardsSearch];
if(lastComma.location != NSNotFound) {
resultString = [resultString stringByReplacingCharactersInRange:lastComma //2
withString: #""];
}
return resultString;
}
It seems that it works, but I get here two warnings:
1. format string is not a string literal (potentially insecure)
2. incompatible pointer types assigning to nsmutablestring from nsstring
How to rewrite it to avoid of warnings?
There is another way to achieve what you are trying to do with fewer lines of code:
You can get an array of NSSet objects using:
NSArray *myArray = [mySet allObjects];
You can convert the array to a string:
NSString *myStr = [myArray componentsJoinedByString:#","];
stringByReplacingCharactersInRange method's return type NSString. You are assigning it to NSMutableString. Use mutable copy.
resultString = [[resultString stringByReplacingCharactersInRange:lastComma //2
withString: #""]mutablecopy]

I have a NSString like this: "Firstname Lastname". How do I convert it to "Firstname L."?

I would like to change it to first name and last initial.
Thanks!
NSString* nameStr = #"Firstname Lastname";
NSArray* firstLastStrings = [nameStr componentsSeparatedByString:#" "];
NSString* firstName = [firstLastStrings objectAtIndex:0];
NSString* lastName = [firstLastStrings objectAtIndex:1];
char lastInitialChar = [lastName characterAtIndex:0];
NSString* newNameStr = [NSString stringWithFormat:#"%# %c.", firstName, lastInitialChar];
This could be much more concise, but I wanted clarity for the OP :) Hence all the interim variables and var names.
This would do it:
NSArray *components = [fullname componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *firstnameAndLastnameInitial = [NSString stringWithFormat:#"%# %#.", [components objectAtIndex:0], [[components objectAtIndex:1] substringToIndex:1]];
This assumes that fullname is an instance of NSString and contains two components separated by whitespace, so you will need to check for that as well.
You can use this code snippet, first separate string using componentsSeparatedByString, then join them again but only get the first character of Lastname
NSString *str = #"Firstname Lastname";
NSArray *arr = [str componentsSeparatedByString:#" "];
NSString *newString = [NSString stringWithFormat:#"%# %#.", [arr objectAtIndex:0], [[arr objectAtIndex:1] substringToIndex:1]];
Get an array of the parts of the name individually:
NSString *sourceName = ...whatever...;
NSArray *nameComponents =
[sourceName
componentsSeparatedByCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
Then, I guess:
NSString *compactName =
[NSString stringWithFormat:#"%# %#.",
[nameComponents objectAtIndex:0],
[[nameComponents lastObject] substringToIndex:1]];
That'll skip any middle names, though if there's only one name, like say 'Jeffry' then it'll output 'Jeffry J.'. If you pass in the empty string then it'll raise an exception when you attempt to get objectAtIndex:0 since that array will be empty. So you should check [nameComponents count].

Resources