How do I get element from NSArray of NSDictionary - nsarray

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");
}

Related

Filter NSDictionary from 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.

Replace a specific word in entire NSString with a special character using reggae

I need to replace a word like DFT with the registered trademark symbol everywhere in a string from a plist.
My code works but always messes up the second one in the same string:
- (NSAttributedString *)addRegisteredTrademarkTo:(NSString*)text
{
NSMutableAttributedString *rawAttString=[[NSMutableAttributedString alloc] initWithString:text];
UIFont *boldFont = [UIFont boldSystemFontOfSize:8];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:#"®"
attributes:#{NSFontAttributeName:boldFont,
NSForegroundColorAttributeName:[UIColor blackColor],
NSBaselineOffsetAttributeName : #4,
NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:5]
}];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"DRT" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,rawAttString.length);
[regex enumerateMatchesInString:text options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:0];
[rawAttString replaceCharactersInRange:subStringRange withAttributedString:attributedString];
}];
NSAttributedString *t = [[NSAttributedString alloc] initWithAttributedString:rawAttString];
return t;
}
String is:Fake name™ with Another FakeDRT building access. SionDRT elevators size
Output is:
The second symbol is always misplaced - and I can't figure out why. Any pointers?

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
}

Custom objects in array stored in a plist

I've got some custom class objects that i store in an array, this array is then stored in a dictionary with a key. This is then saved and will be loaded when the app is loaded/ reloaded.
Now the issues is that i can't seem to get the custom objects back out, when i load the array it comes back as empty. I must be doing something wrong with either writing or loading the data but i can't figure out what exactly.
Here's the code i have so far:
CustomClass.H file:
#import <Foundation/Foundation.h>
#interface PersonClass : NSObject <NSCoding>
#property (nonatomic,strong) NSString *personName;
#property (nonatomic,strong) NSString *personNo;
#property (nonatomic,strong) NSString *personNotes;
#property (nonatomic) BOOL switchPersonality;
#property (nonatomic) BOOL switchChemistry;
#property (nonatomic) BOOL switchLooks;
#property (nonatomic) BOOL switchHumour;
#property (nonatomic) int personRating;
#property (nonatomic,strong) NSNumber *personRecord;
- (id)initWithName:(NSString *)iPersonName PersonNo:(NSString *)iPersonNo PersonNotes:(NSString *)iPersonNotes SwitchChemistry:(BOOL *)iSwitchChemistry SwitchHumour:(BOOL *)iSwitchHumour SwitchLooks:(BOOL *)iSwitchLooks SwitchPersonality:(BOOL *)iSwitchPersonality PersonRating:(int *)iPersonRating PersonRecord:(NSNumber *)iPersonRecord;
#end
Custom Class.M file:
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:personName forKey:#"pName"];
[aCoder encodeObject:personNo forKey:#"pNo"];
[aCoder encodeObject:personNotes forKey:#"pNotes"];
[aCoder encodeInt:personRating forKey:#"pRating"];
[aCoder encodeObject:personRecord forKey:#"pRecord"];
[aCoder encodeBool:switchChemistry forKey:#"sChemistry"];
[aCoder encodeBool:switchHumour forKey:#"sHumour"];
[aCoder encodeBool:switchLooks forKey:#"sLooks"];
[aCoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]){
self.personName = [aDecoder decodeObjectForKey:#"pName"];
self.personNo = [aDecoder decodeObjectForKey:#"pNo"];
self.personNotes = [aDecoder decodeObjectForKey:#"pNotes"];
self.personRating = [aDecoder decodeIntForKey:#"pRating"];
self.personRecord = [aDecoder decodeObjectForKey:#"pRecord"];
self.switchChemistry = [aDecoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [aDecoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [aDecoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [aDecoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
The save Method:
- (void)saveData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
//new array
NSMutableArray *newEntries = [[NSMutableArray alloc]init];
NSLog(#"NEW ENTRIES BEFORE %#",newEntries);
for (PersonClass *person in peopleEntries) {
//encode the object
[NSKeyedArchiver archivedDataWithRootObject:person];
// add the object to the entries
[newEntries addObject:person];
}
NSLog(#"NEW ENTRIES AFTER %#",newEntries);
[newEntries writeToFile:plistPath atomically:YES];
// create dictionary with arrays and their corresponding keys
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSMutableArray arrayWithObjects:newEntries, recordId, nil] forKeys:[NSMutableArray arrayWithObjects: #"peopleEntries",#"recordId", nil]];
NSLog(#"DICTIONARY IS IN SAVE %#",plistDict);
NSString *error = nil;
// check if plistData exists
if(plistDict)
{
// write plistData to our Data.plist file
[plistDict writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
NSLog(#"Save RUN");
}
The load Method:
- (void)loadData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
// check to see if data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// return without loading
NSLog(#"RETURNING");
return;
}
// get saved dictionary
NSDictionary *dictionaryTemp = [[NSDictionary alloc]initWithContentsOfFile:plistPath];
NSLog(#"DICTIONARY IS LOAD %#",dictionaryTemp);
if (!dictionaryTemp)
{
NSLog(#"Error reading plist:");
}
// temporary array
NSMutableArray *holderOne = [dictionaryTemp objectForKey:#"peopleEntries"];
// array to be populated
NSMutableArray *holderTwo = [[NSMutableArray alloc]init];
NSLog(#"HOLDER ONE IS BEFORE %#",holderOne);
NSLog(#"HOLDER TWO IS BEFORE %#",holderTwo);
// go through the array and pull out person classes
for (NSData *person in holderOne) {
// temp array
NSData *entry = [holderOne objectAtIndex:person]; // check the object at index might be an issue???
NSLog(#"ENTRY IS %#",entry);
//deencode the object
[NSKeyedUnarchiver unarchiveObjectWithData:entry];
// add the object to the entries
[holderTwo addObject:entry];
}
NSLog(#"HOLDER ONE IS AFTER %#",holderOne);
NSLog(#"HOLDER TWO IS AFTER %#",holderTwo);
// assign values
peopleEntries = [NSMutableArray arrayWithArray:holderTwo];
NSLog(#"DICTIONARY IS AFTER ADDING %#",dictionaryTemp);
recordId = [NSNumber numberWithInt:[[dictionaryTemp objectForKey:#"recordId"]integerValue]];
NSLog(#"recordId is %#",recordId);
NSLog(#"LOAD RUN");
}
OK i finally discovered the issue, i was extending my class from the NSObject subclass which does not implement the NSCoder protocol so i was encoding and decoding my object in the incorrect way. It should have been done in the following way:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.personName = [decoder decodeObjectForKey:#"pName"];
self.personNo = [decoder decodeObjectForKey:#"pNo"];
self.personNotes = [decoder decodeObjectForKey:#"pNotes"];
self.personRating = [decoder decodeObjectForKey:#"pRating"];
self.switchChemistry = [decoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [decoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [decoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [decoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:personName forKey:#"pName"];
[encoder encodeObject:personNo forKey:#"pNo"];
[encoder encodeObject:personNotes forKey:#"pNotes"];
[encoder encodeObject:personRating forKey:#"pRating"];
[encoder encodeBool:switchChemistry forKey:#"sChemistry"];
[encoder encodeBool:switchHumour forKey:#"sHumour"];
[encoder encodeBool:switchLooks forKey:#"sLooks"];
[encoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
These objects were then added into an array and i was looping through each object in the array and decoding. This was not correct you can simply encode or decode the entire array with te objects as so:
Decode:
NSData *peopleData;
peopleEntries = [NSKeyedUnarchiver unarchiveObjectWithData:peopleData];
Encode:
NSMutableArray *peopleEntries;
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:[[GlobalData sharedGlobalData]peopleEntries]];

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