Update/change values of NSDictionary keys stored in NSArray - objective-c

I have an NSArray containing NSDictionary's of client data. I am using NSPredicate to filter the array and pass it through ViewControllers with segues. I am at a stage where I would like to change some values of the keys stored in the dictionary. How is this achievable?
My prepareForSegue method
if ([[segue identifier] isEqualToString:#"showClients"]) {
// Detect selected row
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// Extract value from the Clinic_Location attribute
NSString *cString = [[_clinicList valueForKey:#"Clinic_Location"] objectAtIndex:indexPath.row];
NSLog(#"Row pressed: %#", cString);
// Set predicate where Clinic_Location equals the clinic string extracted from the selected row
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Clinic_Location == %#", cString];
// Filter the client array using this predicate
filteredClientArray = [dataStart filteredArrayUsingPredicate:predicate];
NSLog(#"Filtered array: %#", filteredClientArray);
// Reload the table
[self.tableView reloadData];
// Sort the array by Appt_Time in ascending order
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:#"Appt_Time" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
NSArray *sortedArray = [filteredClientArray sortedArrayUsingDescriptors:sortDescriptors];
filteredClientArray = [NSMutableArray arrayWithArray:sortedArray];
// Pass the newly filtered & sorted array to next view
PCClientsViewController *pcClients = segue.destinationViewController;
pcClients.clientArray = filteredClientArray;
}
The array now only contains certain dictionaries based on this predicate (i.e. when user selects a row on the TableView, let's say 'Manchester', then only dictionaries with a Clinic_Location of Manchester get passed to the next view.
My prepareForSegue method on the next TableView is similar to the previous one, in this case my predicate filters the dictionary with a matching surname on the selected row and pushes that to the next view. Now I am left with an array constructed like the one below:
Filtered array: (
{
"Appointment_Attended" = Yes;
"Appt_Time" = "2:00";
"Client_Address_Line_1" = "Ap #452-4253 Massa. Street";
"Client_Address_Line_2" = Montebello;
"Client_Address_Line_3" = Hull;
"Client_Address_Line_4" = Quebec;
"Client_Address_Line_5" = Micronesia;
"Client_Forename" = Veronica;
"Client_Postcode" = 69591;
"Client_Surname" = Ellis;
"Client_Tel" = "(019376) 26081";
"Clinic_Location" = Manchester;
"Passed_To_Medical" = No;
"Passed_To_Sol" = No;
}
)
I have tried many different approaches to update specific dictionary keys in this array such as using predicates, but nothing seems to work. For example:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"Client_Address_Line_1 == %#", [[_clientDetails valueForKey:#"Client_Address_Line_1"]objectAtIndex:0]];
NSLog(#"%#", predicate);
NSArray* filteredArray= [_clientDetails filteredArrayUsingPredicate: predicate];
[filteredArray performSelector: #selector(setValue:forKey:) withObject:self.clientAddLine1.text withObject:#"Client_Address_Line_1"];
No matter what I try I am continuously getting error messages such as:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x7904f590> setValue:forUndefinedKey:]: this
class is not key value coding-compliant for the key
Client_Address_Line_1.'
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI
setObject:forKey:]: unrecognized selector sent to instance 0x78774c70'
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSDictionaryI 0x7bff6eb0> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key
Client_Address_Line_1.'
It seems no matter what I do I cannot change or update any dictionary keys within the array. After hours of research, I'm beginning to wonder if this is this even possible? Am I looking at the wrong approach completely? Is there a better way to do this? After the keys have been changed, I need to save them back to the array and pass it back through ViewControllers also (possibly with Unwind Segues?) Any help is much appreciated.

You may only change the values for key in mutable subclasses of NSDictionary, NSDictionary itself is immutable.
so since you have changed to a mutable array, you can do the same thing for each of the dictionaries also:
filteredClientArray = [NSMutableArray arrayWithArray:sortedArray];
for(NSDictionary *d in [filteredClientArray copy])
{
NSMutableDictionary * m = [d mutableCopy];
[filteredClientArray replaceObjectAtIndex:[filteredClientArray indexOfObject:d] withObject:m];
}

Related

NSUnknownKeyException reason: '[<store 0x17572b80> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name

I am devoleping an application for creating a search bar above contacts list.If i press the search bar app gets crashed .I debugged the application and found that the crash occuars at this line. filteredCandyArray = [NSMutableArray arrayWithArray:[candyArray filteredArrayUsingPredicate:predicate]];
What I'm doing in my program is I'm fetching all contacts(name,no.and image) and adding to an array(candyArray) .But when i try to do an NSPredicate operation,i get this message and application crashes by giving the below message.
wetwert[2380:60b] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
* First throw call stack:
(0x2d51ae83 0x378776c7 0x2d51ab89 0x2ded6e3f 0x2de3c139 0x2de7a3f7 0x2de79fb5 0x2de79083 0x2de7901f 0x2de78e2d 0x27d23 0x27f37 0x2fe92b79 0x2fcd3da3 0x2fcd3d3f 0x2fcd3d13 0x2fcbf743 0x2fe928ed 0x2fcdd8b1 0x2fe53b67 0x2fe5310f 0x2fe92727 0x2fe52e2d 0x2de49aa5 0x2fce1485 0x2fe52d5f 0x2fcc9049 0x2defee4b 0x2d4e5f1f 0x2d4e53e7 0x2d4e3bd7 0x2d44e471 0x2d44e253 0x321882eb 0x2fd03845 0x28451 0x37d70ab7)
libc++abi.dylib: terminating with uncaught exception of type NSException
I'm giving the code for adding to array.below
for (int i=0; i<_contactsarray.count; i++) {
[candyArray addObject:[store candyofcategory:[_contactsarray objectAtIndex:i] phone:[_numbersarray objectAtIndex:i] image:[_imagesarray objectAtIndex:i]]];
where the 3 arrays contain name,phone no . and images.
The method which gets called is shown below.
+(id)candyofcategory:(NSString *)name phone:(NSString *)phone image:(UIImage *)image{
store *newstore=[[self alloc]init];
newstore.c_name=name;
newstore.c_phone=phone;
newstore.cnt_image=image;
return newstore;
}
this method is in a class 'store' which is of NSObject class.Im able to fetch data and display in a tableview,but when i call the predicate operation on this array ,i get a crash.Any ideas on the problem? Im adding the Predicate method below
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredCandyArray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
filteredCandyArray = [NSMutableArray arrayWithArray:candyArray];
filteredCandyArray = [NSMutableArray arrayWithArray:[candyArray filteredArrayUsingPredicate:predicate]];
}
If I see it correctly, store has a property "c_name" (and not "name").
So you should use that in the predicate:
[NSPredicate predicateWithFormat:#"SELF.c_name CONTAINS[c] %#",searchText]

Prevent duplicate printout of element in NSMutableArray

I am using a singleton class to transfer a NSMutableArray across views.
The issue I am having is my array is getting displayed multiple times.
Right now I am working with three UITextFields. Each is getting added to the array and outputted in a specific format. Here is what my output looks like:
A / B
C
A / B
C
A / B
C
All I need shown is:
A / B
C
Here is my code if someone can help me find what is missing or needs to be reworked:
To display the text:
UILabel * myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
[myLabel setFont:[UIFont fontWithName:#"Helvetica" size:10.0]];
myLabel.numberOfLines = 0;
NSMutableString * string = [NSMutableString string];
Education * myEducation = [Education sharedEducation];
for (Education * output in myEducation.educationOutputArray)
{
[string appendString:[NSString stringWithFormat:#"%# / %# \n%#\n", [myEducation.educationOutputArray objectAtIndex:0], [myEducation.educationOutputArray objectAtIndex:1], [myEducation.educationOutputArray objectAtIndex:2], nil]];
}
myLabel.text = string;
[self.view addSubview:myLabel];
Here is where I am saving the text:
-(void)saveButtonTapped:(id)sender
{
[_educationArray addObject:_aTextField.text];
[_educationArray addObject:_bTextField.text];
[_educationArray addObject:_cTextField.text];
Education * myEducation = [Education sharedEducation];
myEducation.educationOutputArray = _educationArray;
[self.navigationController popViewControllerAnimated:YES];
}
EDIT *
When a user is entering text into the text fields they can also add a new set of text using the same three text fields.
If I removed the for loop only the first is displayed. How can I get all of the text to display?
EDIT TWO *
I tried this:
[string appendString:[NSString stringWithFormat:#"%# / %# \n%#\n", [output major], [output university], [output timeAtSchool]]];
Results in this error:
2012-12-29 17:03:07.928 EasyTable[14501:c07] -[__NSCFString major]: unrecognized selector sent to instance 0x7176850
2012-12-29 17:03:07.941 EasyTable[14501:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString major]: unrecognized selector sent to instance 0x7176850'
*** First throw call stack:
(0x1c99012 0x10d6e7e 0x1d244bd 0x1c88bbc 0x1c8894e 0x6162 0xff817 0xff882 0xffb2a 0x116ef5 0x116fdb 0x117286 0x117381 0x117eab 0x1184a3 0x118098 0x2c10 0x10ea705 0x21920 0x218b8 0xe2671 0xe2bcf 0xe1d38 0x2e5213 0x1c61afe 0x1c61a3d 0x1c3f7c2 0x1c3ef44 0x1c3ee1b 0x1bf37e3 0x1bf3668 0x1e65c 0x240d 0x2335 0x1)
libc++abi.dylib: terminate called throwing an exception
You add three elements at a time to the array, and later you want to process them three at a time.
It would probably be better to create a container class that holds the three elements, and add instances of the container class to the array.
But if you really want to do it this way, you can't use a for/in loop. You have to use an index variable:
NSArray *array = myEducation.educationOutputArray;
for (NSUInteger i = 0, count = array.count; i < count; i += 3) {
[string appendFormat:#"%# / %#\n%#\n", array[i], array[i+1], array[i+2]];
}
you don't have duplicates in the array: the problem is in the for loop
for (Education * output in myEducation.educationOutputArray)
{
[string appendString:[NSString stringWithFormat:#"%# / %# \n%#\n", [myEducation.educationOutputArray objectAtIndex:0], [myEducation.educationOutputArray objectAtIndex:1], [myEducation.educationOutputArray objectAtIndex:2], nil]];
}
here you are looping through the array 3 times (because there are 3 objects in the array) and then printing each object each time. I think you should just do the appendString: line wihtout the for loop
Your for loop is really odd; You loop through each object in the array but then you hardcoded the indexes inside the for loop. I'm not sure what's going on there but it's certainly the source of the odd output. That pointer to an Education object is what you should access. Each time through the loop, use the value of output.
If you're also having uniqueness issues, consider using an NSSet instead of an NSMutableArray. If you care about order, use NSOrderedSet. Set collection types do not allow duplicated values.
Also one more point: If you know you're only going to ever access the values at index 0, 1, and 2, there's really no point to having an NSArray. Just store each value separately and access it directly. At the very least there's no reason for the for loop.
Edit:
You're crashing because you think your array has Education objects in it but it really has plain strings:
-[__NSCFString major]: unrecognized selector sent to instance 0x7176850
Notice that it's saying that you tried to call the method major on an __NSCFString (a private subclass of NSString that apple uses internally). This is a good indicator that your array doesn't contain what you think it does. Make sure you're loading your data into your array correctly by constructing Education objects with the three pieces of data.
You also suggested that you tried
[string appendString:[NSString stringWithFormat:#"%# / %# \n%#\n", output]];
Basically what stringWithFormat: does is take that format string and use it like a template and then it takes as many arguments as you need to fill up the placeholders in the template. %# is a placeholder for an object's description so stringWithFormat: is going to expect three parameters after the format string to fill each %#.

How do I append an object to NSMutableArray?

I want to update an mutable array. i have one array "ListArray" with some keys like "desc", "title" on other side (with click of button.) i have one array name newListArray which is coming from web service and has different data but has same keys like "desc" "title". so i want to add that data in "ListArray" . not want to replace data just add data on same keys. so that i can show that in tableview.
..........So my question is how to add data in "ListArray". or any other way to show that data in tableview but replacing old one just want to update the data
NSMutableArray *newListArray = (NSMutableArray*)[[WebServices sharedInstance] getVideoList:[PlacesDetails sharedInstance].userId tokenValue:[PlacesDetails sharedInstance].tokenID mediaIdMin:[PlacesDetails sharedInstance].minId mediaIdMax:[PlacesDetails sharedInstance].maxId viewPubPri:#"Public_click"];
NSDictionary *getNewListDic = [[NSDictionary alloc] initWithObjectsAndKeys:newListArray,#"videoList", nil];
[listVidArray addObject:getNewListDic];
NSArray *keys = [NSArray arrayWithObjects:#"desc",#"url",#"media_id",#"img",#"status",#"media_id_max",#"fb_url",#"title", nil] ;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for(int i = 0 ; i < [listVidArray count] ; i++)
{
for( id theKey in keys)
{
// NSMutableArray *item = [NSMutableArray array];
NSLog(#"%#",theKey);
NSLog(#"%#",keys);
[dict setObject:[[[listVidArray objectAtIndex:i]valueForKey:#"videoList"] valueForKey:theKey] forKey:theKey];
// [dict setObject:[newListArray valueForKey:theKey] forKey:theKey];
}
}
If you the newListArray is totally different from the oldListArray, then clear the old one, and use the new data to fit it.
Otherwise, you need to merge the two arrays. One way is to check if a
data in newListArray is/is not in oldListArray and then decide
whether to add it into oldListArray.
When oldListArray is updated, call -reloadData of the tableView.
If I do not misunderstand your question, you may do something like this:
[listArray addObjectsFromArray:newListArray];
[_tableView reloadData];

objective-c empty NSArray error

I'm using JSON to parse data into an NSArray 'courseArray'. I then filter this data using NSPredicate and store it into 'rows'. The problem is, i dont know how to determine if 'rows' is empty or not. For example : If a record exists, rows will contain the appropriate objects, but if the record doesnt exist then the application crashes (also when i use NSLog to see the contents of the array its blank, i believe its supposed to say its nil? if there are no objects). How can i fix this?
if (dict)
{
courseArray = [[dict objectForKey:#"lab"] retain];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"labNumber== %#",labSelected]; //filter
rows = [[courseArray filteredArrayUsingPredicate:predicate]retain];
}
[jsonreturn release];
self.formattedTextView.opaque = NO;
self.formattedTextView.backgroundColor = [UIColor clearColor];
NSLog(#"array %#",rows);
**NSDictionary *currentSelection = [rows objectAtIndex:0];** (app crashes at this line - [NSArray objectAtIndex:]: index 0 beyond bounds for empty array')
NSString *labNumber = [currentSelection objectForKey:#"labNumber"];
-objectAtIndex: will raise an exception if there is no object at the nominated index. If rows is empty there won't be an object at index 0, hence the exception. As you don't catch the exception, the result is that your app is terminated.
You probably want to compare [rows count] to 0 to check that there's something in it and to react appropriately if not.
Try if([courseArray count] > 0) also another method that can be of help here is
[courseArray containsObject:(id)]

Problem sorting NSMutableArray

I am aware that there is a lot of questions about this topic, and i do apologize for that as i just cant get this to work for my NSMutableArray. I have problem to really understand the sorting and i have been reading documentation
I have an NSMutableArray with the following type of data:
Player name
score
Player name
score
Player name
score
...
It is a combination between a name and a score (NSNumber). I am trying to find a way to sort this based on the score. I have read a lot but i just do not get this to work, i also have problem to understand the sort concept. I have tried to sort the full array but...
I would very much appreciate if someone can give me a short, understandable, explanation of the sort scenario for this and also an example of how to sort this.
Edit: I changed to dictionary, selected the values and was thinking to sort the allObjects (stored as NSNumber in the dict) and then just select the key from the dict based on the sorted object.
NSArray *allPlayers = [playerResultInTheGame allKeys];
NSArray *allObjects = [playerResultInTheGame allValues];
NSLog(#"allPlayers: %#", allPlayers);
NSLog(#"allObjects: %#", allObjects);
NSMutableArray *sortedArray = [allObjects sortedArrayUsingSelector:#selector(Compare:)];
I get the following when i run this:
2011-01-16 21:10:08.417 XX[6640:207] playerResultInTheGame: {
Barnspelare = 3;
Vuxenspelare = 3;
}
2011-01-16 21:10:08.418 XX[6640:207] allPlayers: (
Barnspelare,
Vuxenspelare
)
2011-01-16 21:10:08.418 XX[6640:207] allObjects: (
3,
3
)
2011-01-16 21:10:08.419 XX[6640:207] -[NSCFNumber Compare:]: unrecognized selector sent to instance 0x5b26f10
2011-01-16 21:10:08.422 XX[6640:207] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFNumber Compare:]: unrecognized selector sent to instance 0x5b26f10'
Can someone please advice as i do not really understand this?
What you need to do here is to store your scores and player names in a dictionary. Use the player name as the key (unless the same player is included more than once) and the score as the value. Then, sorting the players is as easy as this:
NSDictionary *dict; // initialize dictionary with key/value pairs
NSArray *allPlayers = [dict allKeys];
Sort the allPlayers array however you want, then get the scores out of the dictionary.
Lets go.
1) You need to create array of dictionaries. 2) Then sort it. 3) Then create final array of strings and numbers from sorted array of dictionaries.
//for example you have this
NSArray *allPlayers = #[#"John",#"Carl",#"Elena",#"Anna"];
NSArray *allAges = #[#30, #25, #16, #21];
//created storage to sort results
NSMutableArray *sortedPlayers = [NSMutableArray new];
[allPlayers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//I do not know if you have same count of player names and ages allways
//so just to prevent app crash check this situation
if (allAges.count <= idx){
*stop = YES;
return ;
}
[sortedPlayers addObject:#{
#"name":obj,
#"age":allAges[idx]
}];
}];
//let the sort begin
[sortedPlayers sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumber *age1 = obj1[#"age"];
NSNumber *age2 = obj2[#"age"];
//just leave comparing to the NSNumber object
return [age1 compare:age2];
}];
NSLog(#"Wow. It works!\n:%#",sortedPlayers);
//now you need the initial array with player\age values
NSMutableArray *result = [NSMutableArray new];
//append each pair to result
for (NSDictionary *obj in sortedPlayers){
NSString *name = obj[#"name"];
NSNumber *age = obj[#"age"];
[result addObject:name];
[result addObject:age];
}
NSLog(#"The final result is\n:%#",result);
This will produce the following result in console:
(
Elena,
16,
Anna,
21,
Carl,
25,
John,
30
)