Filter NSDictionary from NSArray - nsarray

I have One NSArray That contains NSDictionary Object with keys{"name","age","weight"}
where the key name is NSString ,age is NSInteger ,weight is float Value
I need to filter the NSArray with the following conditions
1.name contains 'abc'
2.age below 18
3.weight less than 50
Answer will be Appreciated

This will work but is not elegant at all.
NSMutableArray *filteredArray = [[NSMutableArray alloc]init];
for (NSDictionary *dic in myArray){
int age = [[dic objectForKey:#"age"]intValue];
if (age < 18){
float weight = [[dic objectForKey:#"weight"]floatValue];
if (float < 50){
string name = [dic objectForKey:#"name"];
if (![string rangeOfString:#"bla"].location == NSNotFound) {
[filteredArray addObject:dic];
}
}
}
}
The other elegant solution is to use the enumeration block and predicates to check your dictionary contents, but I'm not comfortable enough in objective-C to do that.

Related

Regular expression for custom types

I'm interested to create a new grammar and build REGEX for the grammar.
Grammar is set of laws that allowing word or not.
For exemple i have create 5 types : A, B, C, D, E.
My grammar is all word that like ABC , ABE , ABDC, ABDE .
(A and B must, D optional and need choose C or E).
How can i create REGEX to check if the word is correct for the custom grammar?
(preference for c/c++ or objective C code)
Thanks a lot.
A = ice cream, B = cone, D = whipped cream, C = chocolate and E = vanilla.
So ice cream and cone are a must, whipper cream optional and need to know chocolate or vanilla to accept the expression
Try this
(AB(C|E)D?)|(ABD?(C|E))
For objective C:
NSString *searchedString = #"ice creamconewhipped creamchocolate";//NOTE: here i dont put space
NSString *A = #"ice cream";
NSString *B = #"cone";
NSString *D = #"whipped cream";
NSString *C = #"chocolate";
NSString *E = #"vanila";
NSString *stringRegExp = [NSString stringWithFormat:#"(%#%#(%#|%#)%#?)|(%#%#%#?(%#|%#))",A,B,C,E,D,A,B,D,C,E];//(AB(C|E)D?)|(ABD?(C|E))
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", stringRegExp];
if ([myTest evaluateWithObject: searchedString])
{
NSLog(#"MATCHING");
}
else
{
NSLog(#"NOT_MATCHING");
}
Equivalent Case for Class:
HomeViewController *objA = [[HomeViewController alloc]init];
NSString *className1 = NSStringFromClass([objA class]);
TableViewController *objB = [[TableViewController alloc]init];
NSString *className2 = NSStringFromClass([objB class]);
CustomTextView *objD = [[CustomTextView alloc]init];
NSString *className4 = NSStringFromClass([objD class]);
CustomTableViewCell *objC = [[CustomTableViewCell alloc]init];
NSString *className3 = NSStringFromClass([objC class]);
NSString *objE;
NSString *searchedStringABDC = [NSString stringWithFormat:#"%#%#%#%#",className1,className2,className3,className4];//CASE ABDC/ ABCD
NSString *A = NSStringFromClass([HomeViewController class]);
NSString *B = NSStringFromClass([TableViewController class]);
NSString *D = NSStringFromClass([CustomTextView class]);
NSString *C = NSStringFromClass([CustomTableViewCell class]);
NSString *E = NSStringFromClass([AssementAndPlanView class]);
NSString *stringRegExp = [NSString stringWithFormat:#"(%#%#(%#|%#)%#?)|(%#%#%#?(%#|%#))",A,B,C,E,D,A,B,D,C,E];//(AB(C|E)D?)|(ABD?(C|E))
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", stringRegExp];
if ([myTest evaluateWithObject: searchedStringABDC])
{
NSLog(#"MATCHING");
}
else
{
NSLog(#"NOT_MATCHING");
}
or if you doesn't need the correct order, without using regex , u can check with isKindOfClass . For Example:
if ([objA isKindOfClass:[HomeViewController class]]&&[objB isKindOfClass:[TableViewController class]]&&[objC isKindOfClass:[CustomTableViewCell class]]&&!objD&&!objE)
{
//case ABC
NSLog(#"case ABC");
}
else if ([objA isKindOfClass:[HomeViewController class]]&&[objB isKindOfClass:[TableViewController class]]&&[objE isKindOfClass:[AssementAndPlanView class]]&&!objC&&!objD)
{
//case ABE
NSLog(#"case ABE");
}
you can try this regular:
ABD?[CE]

How do I get element from NSArray of NSDictionary

This is NSLog of my NSArray
[{"id":16,"venueId":16,"street":"171 - 3401 Dufferin St","city":"Toronto","zipcode":"M6A 2T9","province":"ON","country":"Canada"}]
NSDictionary *dict = [myarray objectAtIndex:i]
//myarray is your array of dictionary
//if the array has just one element like in your example, i will be 0
NSNumber *venueId = [dict objectForKey:#"venueId"];
finally I found solution
NSError *error;
NSArray* jsonArray = [NSJSONSerialization JSONObjectWithData:[[venue objectForKey:#"address"] dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error] ;
NSDictionary *dict = [jsonArray objectAtIndex:0];
//myarray is your array of dictionary
NSString *street = [dict objectForKey:#"street"];
NSLog(#"street: %#", street);
Note: I used below code to check data type to make sure it returns valid kind of class.
if ([[venue objectForKey:#"address"] isKindOfClass:[NSArray class]]) {
NSLog(#"%#", #"It is NSArray");
} else if ([[venue objectForKey:#"address"] isKindOfClass:[NSString class]]) {
NSLog(#"%#", #"It is NSString");
}

stringValue of NSDictionary objectForKey runtime error

So I have the following code that causes a runtime error in XCode
NSString* temp = [[param objectforkey#"firstParam"] stringValue];
int tempNum = [[param objectforkey#"secondParam"] intValue];
param loads from a plist. firstParam is as string, secondParam is a number
First line crashes the program.
Now what's interesting is it works if I do a hard caste i.e:
NSString* temp = (NSString*)[param objectforkey#"firstParam"];
int tempNum = [[param objectforkey#"secondParam"] intValue];
Just wondering why the id would have inconsistent implementation in that I have to use intValue to cast to int, but have to do hard cast to get NSString? Why not stringValue?
Your first call to objectForKey returns an object which is an NSString. NSString doesn't have a stringValue method which is why a runtime error is generated. It doesn't make sense to get the stringValue of something that is already a string.
The second call to objectForKey returns an NSNumber. NSNumber does have an intValue method so it doesn't cause an error.
In the second case you are changing the type of the returned value from NSNumber to int. In the first case the object returned is already an NSString and there is no point trying to get the stringValue of a string.
The call [param objectforkey#"firstParam"] DOES return a NSString, so there's no need to call stringValue. Furthermore, NSString does not have a method called "stringValue", so that's the source of your problem.
Just call this: NSString *temp = (NSString *)[param objectForKey:#"firstParam"];
Also note that the selector is objectForKey:, not objectforkey: (capitalization matters).
And, answering your question, NSNumber is a class, so any object is not an "int", but a NSNumber pointer. intValue returns an actual "int", as floatValue returns a "float" (NSNumber can represent any type of number). In the case of the NSString that's not necessary, because there is only one type of NSString (there is, though, a method for returning a const char *, and that would be used like char *string = [[param objectForKey:#"firstParam"] cString];... not very useful for Objective-C apps anyway).
Lets say you call a web service that returns JSON
Then you process data
NSDictionary *json_response = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
now you try something like this
NSString *name = [[object objectForKey:#"Name"] stringValue];
then you get the error....
As mttrb mentions you get the error because of stringValue, objectForKey default return value is String so a potential fix in this case would be:
NSString *name = [object objectForKey:#"Name"];
Hope this hints some one!
If you can not be sure that the returned value is a NSString you should do this:
id value = [param objectForKey#"firstParam"];
if ([value isKindOfClass:[NSString class]]) {
NSString *temp = (NSString *)id;
//Handle string
}
else {
//Handle other case
}
Completing the answer of #Torge, I add my solution to get always an NSString from objectForKey method:
NSObject * value = [param objectForKey#"firstParam"];
NSString *stringValue;
if ([value isKindOfClass:[NSString class]]) {
//Handle string
stringValue = (NSString *)value;
}
else if([value isKindOfClass:[NSNumber class]]){
//Handle NSNumber
stringValue = [((NSNumber *) value) stringValue];
}
else {
//Handle other case
}

fast enumeration and selecting objects

I've an array (tempList) populated with records read from a sqlite data source. I want to sort these records based on 'region'. So I've set up the following NSMUtableArrays: _Asia, _Africa, _CentralAmerica, _southAmerica.
The method compiles without any errors. Where the 'if' statements return true, it executes the 'addObject' but the element is not added to the array.
- (void) sortIntoRegions: tempList
{
for (beans *arrayElement in tempList) {
NSLog(#"region: %#", arrayElement.region);
if ([arrayElement.region isEqualToString:#"Africa"]) {
[_africa addObject:arrayElement.name];
} else if ([arrayElement.region isEqualToString: #"Asia & South Pacific"]) {
[_asia addObject:arrayElement.name];
}
else if ([arrayElement.region isEqualToString: #"Central America"]) {
[_centralAmerica addObject:arrayElement];
} else if ([arrayElement.region isEqualToString: #"South America"]) {
[_southAmerica addObject:arrayElement];
}
}
}
I'm going to go out on a limb and guess that you have declared _africa, _asia, _centralAmerica, and _southAmerica to be NSMutableArray *, but you have not initialized them. That is, you have not written any code like this:
_africa = [[NSMutableArray alloc] init];
_asia = [[NSMutableArray alloc] init];
_centralAmerica = [[NSMutableArray alloc] init];
_southAmerica = [[NSMutableArray alloc] init];
Your instance variables are set to nil when your object is created. Sending a message (like addObject:) to nil has no effect (and doesn't print a warning or error message).

How do you use NSRegularExpression's replacementStringForResult:inString:offset:template:

I have a series of characters that I want to match with a regular expression, and replace them with specific strings depending on what they are.
Example:
In => "This is the input string where i want to replace 1 2 & 3"
Out => "This is the input string where i want to replace ONE TWO & THREE"
I currently do this by splitting the string using spaces as the separator, and parsing each string individually, incrementally rebuilding the string. I feel this is ugly, and lacking imagination, and kinda slow.
According the Apple documentation I should be able to do this using the replacementStringForResult:inString:offset:template: method. However I can't seem to understand how to use it correctly.
You can use the method within a for in loop using matchesInString:options:range:, which returns an array of matches as NSTextCheckingResults:
NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:#"\\b[1-3]\\b"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSString* yourString = #"This is the input string where i want to replace 1 2 & 3";
NSMutableString* mutableString = [yourString mutableCopy];
NSInteger offset = 0; // keeps track of range changes in the string
// due to replacements.
for (NSTextCheckingResult* result in [regex matchesInString:yourString
options:0
range:NSMakeRange(0, [yourString length])]) {
NSRange resultRange = [result range];
resultRange.location += offset; // resultRange.location is updated
// based on the offset updated below
// implement your own replace functionality using
// replacementStringForResult:inString:offset:template:
// note that in the template $0 is replaced by the match
NSString* match = [regex replacementStringForResult:result
inString:mutableString
offset:offset
template:#"$0"];
NSString* replacement;
if ([match isEqualToString:#"1"]) {
replacement = #"ONE";
} else if ([match isEqualToString:#"2"]) {
replacement = #"TWO";
} else if ([match isEqualToString:#"3"]) {
replacement = #"THREE";
}
// make the replacement
[mutableString replaceCharactersInRange:resultRange withString:replacement];
// update the offset based on the replacement
offset += ([replacement length] - resultRange.length);
}
NSLog(#"mutableString: %#", mutableString); // mutableString: This is the input string where i want to replace ONE TWO & THREE
The answer of Dano works perfectly and following the idea of Pedro in the comments I wrapped the code into a category that takes a block that does the replacement part.
This is very handy to use.
NSRegularExpression+Replacement.h
#interface NSRegularExpression (Replacement)
- (NSString *)stringByReplacingMatchesInString:(NSString *)string
options:(NSMatchingOptions)options
range:(NSRange)range
template:(NSString *)templ
withMatchTransformation: (NSString* (^) (NSString* element))transformation;
#end
NSRegularExpression+Replacement.m
#implementation NSRegularExpression (Replacement)
- (NSString *)stringByReplacingMatchesInString:(NSString *)string
options:(NSMatchingOptions)options
range:(NSRange)range
template:(NSString *)templ
withMatchTransformation: (NSString* (^) (NSString* element))transformation
{
NSMutableString* mutableString = [string mutableCopy];
NSInteger offset = 0; // keeps track of range changes in the string due to replacements.
for (NSTextCheckingResult* result in [self matchesInString:string
options:0
range:range])
{
NSRange resultRange = [result range];
resultRange.location += offset; // resultRange.location is updated based on the offset updated below
// implement your own replace functionality using
// replacementStringForResult:inString:offset:template:
// note that in the template $0 is replaced by the match
NSString* match = [self replacementStringForResult:result
inString:mutableString
offset:offset
template:templ];
// get the replacement from the provided block
NSString *replacement = transformation(match);
// make the replacement
[mutableString replaceCharactersInRange:resultRange withString:replacement];
// update the offset based on the replacement
offset += ([replacement length] - resultRange.length);
}
return mutableString;
}
#end
And here is how you use it to solve the initial question:
NSString* yourString = #"This is the input string where i want to replace 1 2 & 3";
NSError* error = nil;
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:#"\\b[1-3]\\b"
options:0
error:&error];
return [regex stringByReplacingMatchesInString:yourString options:0 range:NSMakeRange(0, [yourString length]) template:#"$0" withMatchTransformation:^NSString *(NSString *match) {
NSString* replacement;
if ([match isEqualToString:#"1"]) {
replacement = #"ONE";
} else if ([match isEqualToString:#"2"]) {
replacement = #"TWO";
} else if ([match isEqualToString:#"3"]) {
replacement = #"THREE";
} else {
return replacement;
}
}];
You should use
- (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template
or
- (NSUInteger)replaceMatchesInString:(NSMutableString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template
with string being "This is the input string where i want to replace 1 2 & 3" and template being either "ONE", "TWO" or "THREE".
I needed a more generic solution for the same problem so I refined Dano's answer to a method like this, with a sample usage explained below:
- (NSMutableString *)replaceSubstringsInString:(NSString*)string
usingRegex:(NSString*)searchRegex
withReplacements:(NSDictionary*)replacements {
NSMutableString *newString = [string mutableCopy];
__block NSInteger offset = 0;
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:searchRegex
options:0
error:&error];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange resultRange = [match range];
resultRange.location += offset;
NSString *substring = [string substringWithRange:match.range];
__block NSString* replacement;
[replacements enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([key isEqualToString:substring]) {
replacement = obj;
}
}];
[newString replaceCharactersInRange:resultRange withString:replacement];
offset += ([replacement length] - resultRange.length);
}];
return newString;
}
Usage:
NSString *string = #"This is the input string where i want to replace 1 2 & 3";
NSString *searchRegex = #"\\b[1-3]\\b";
NSDictionary *replacements = #{#"1":#"ONE",#"2":#"TWO",#"3":#"THREE"};
NSMutableString *result = [self replaceSubstringsInString:string
usingRegex:searchRegex
withReplacements:replacements];
Explanation:
You just have to pass in the string to search for the matching substrings, along with the desired regexSearch pattern and a replacements dictionary containing the string pairs with the key being the substring to be replaced and the object with the desired replacement string.
Output:
// This is the input string where i want to replace ONE TWO & THREE
I was looking for something similar, but didn't like most of the answers here, so I wrote something inspired by how PHP does string replacement:
#implementation NSString (preg_replace)
- (instancetype)stringByReplacingMatchesFromRegularExpression:(NSRegularExpression *)regularExpression replacementBlock:(NSString * (^)(NSArray *matches))replacementBlock
{
NSMutableString *finalString = self.mutableCopy;
NSUInteger offset = 0;
for (NSTextCheckingResult *match in [regularExpression matchesInString:self options:0 range:NSMakeRange(0, self.length)]) {
NSMutableArray *matches = [NSMutableArray array];
for (NSUInteger index = 0; index < match.numberOfRanges; ++index) {
[matches addObject:[self substringWithRange:[match rangeAtIndex:index]]];
}
NSString *replacementString = replacementBlock(matches.copy);
[finalString replaceCharactersInRange:NSMakeRange(match.range.location + offset, match.range.length) withString:replacementString];
offset += replacementString.length - match.range.length;
}
return finalString;
}
#end
To use it:
NSRegularExpression *expression = [[NSRegularExpression alloc] initWithPattern:#"[0-9A-F]{2}" options:0 error:nil];
NSString *string = #"AB-DE-EF";
NSString *result = [string stringByReplacingMatchesFromRegularExpression:expression replacementBlock:^NSString *(NSArray *matches) {
return [NSString stringWithFormat:#"(%#)", [matches[0] lowercaseString]];
}];
NSLog(#"Result = %#", result); // "(ab)-(de)-(ef)"
I recommend this, much shorter and uses a little memory :
Another solution