Updating NSDictionary inside an NSArray - ios

I have a NSArray of NSDictionaries. I need the updated NSArray of NSDictionaries with value of one of the dictionary key updated with new value.
Please see below the NSArray structure. I want to update the value for Key3 inside this structure if Key1 matches with some value (e.g. 2). What would the fastest way of doing this. I do not want to use traditional For loop.
[myArray valueForKeyPath:#"#unionOfObjects.Key3"];
<__NSCFArray 0xe70c10>(
{
Key1 = 1;
Key2 = "New Location";
Key3 = (
Data1,
Data2,
Data3
);
},
{
Key1 = 2;
Key2 = "Old Location";
Key3 = (
Data1,
Data2,
Data4
);
}
)

We can solve it by using predicates:
NSArray *dictionaryArray;
NSNumber *key =#2;
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"Key1=%#",key];
NSArray *result =[dictionaryArray filteredArrayUsingPredicate:predicate];
result array now has all dictionaries having (key1 = 2)
Dictionarys can't be edited directly, they must be NSMutableDictionary to edit. Assuming they are NSMutableDictionary instances:
NSMutableDictionary *dic =result[0];
[dic setObject:someObject forKey:#"key3"];

Let's suppose that you already have an array containing all mutable dictionaries. The first step is to get what dictionaries you need to change:
NSPredicate* predicate= [NSPredicate predicateWithFormat: #"Key1=2"];
NSArray* filteredArray= [myArray filteredArrayUsingPredicate: predicate];
The second step is to replace the Key3 value for each object in the array. If you execute a selector on an array, and NSArray doesn't respond to that selector, the selector is performed on it's objects:
[filteredArray performSelector: #selector(setValue:forKey:) withObject: someValue withObject: #"Key3"];
After this statement you don't need to replace any object in myArray, because the filtered array contains the same objects that are in myArray, which are mutable dictionaries.

I would also go for predicates as #santhu stated on his answer. However, I want to point that when searching an unsorted array linear search (that is, looping through every object and checking if it's the wanted one) is optimal. Probably predicates will provide an speed-up due to their internal routines, but a predicate will still execute the "traditional for loop" you want to avoid and thus the speed-up will not be very significant.
There are other searching algorithms which provide a significant speed-up, but they are only valid for sorted databases (unless you have a quantum iPhone ;) ).
If you are interested on this topic, here you can find some types of search algorithms with an analysis of their complexity.

Related

nsdictionary with arrays nspredicate filtering

I have a tableview that i want to search through with a searchable. It worked before but when i added sections i got into trouble because i had to change from arrays to dictionary.
So basically i have a NSDictionary that looks like this
{ #"districtA": array with point objects, #"districtB": array with point objects}
I need to filter them based on the point objects.name that is in the arrays. After that i want to create a new nsdictionary with the filtered objects in it.
I tried at least 10 different methods but i can't figure it out so i think this is the only way that i am most positive that should work.
This is the only way i can think of if there is an easier way or more logic way please tell me.
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//create new array to fill
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
//loop through the values and put into an rray based on the predicate
arrayWithFilteredPoints = [NSArray arrayWithObject:[[self.PointList allValues] filteredArrayUsingPredicate:predicate]];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:Point.district])
dict[Point.district] = [#[] mutableCopy];
[dict[Point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];}
This results in a crash, it happens of course where the predicate is used but i don't know how to debug what predicate i should use:
on 'NSInvalidArgumentException', reason: 'Can't do a substring operation with something that isn't a string (lhs = (
West ) rhs = w)'
I have read the comments and you are right. There was something wrong with my code.
I changed the logic, i added some more steps (like creating NSArray without needing it) to make the solution clear
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//1. create new array to fill only the Points from the dictionary
NSArray *allPoints = [self.PointList allValues];
NSMutableArray *allPointObjects = [[NSMutableArray alloc]init];
for (NSArray *array in allPoints) {
for (Point *point in array) {
[allPointObjects addObject:point];
}
}
//2. loop through allPointObjects and put into an mutablearray based on the predicate
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
arrayWithFilteredPoints = [allPointObjects filteredArrayUsingPredicate:predicate];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:point.district])
dict[point.district] = [#[] mutableCopy];
[dict[point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I wanted a filtered nsdictionary at the end that i can pass back to my tableview that reads the dictionary objects based on the keys (districts)
It seems clear from your description that [self.PointList allValues] is not an array of Point objects but an array of arrays of Point objects. That is the source of your difficulty, including your original crash.
You need to decide what to do about that; for example, if you want just one big array of Point objects, then flatten the array of arrays before you filter. I can't advise you further because it is not obvious to me what ultimate outcome you desire.
EDIT You've now modified your code and I can see more clearly what you're trying to do. You have a dictionary whose values are arrays of points, and you are trying to filter some of the points out of each array. What I would have done is to do that - i.e., run thru the keys, extract each array, filter it, and put it back (or delete the key if the array is now empty). But I can see that what you are doing should work, because you have cleverly put the keys into the points to start with, so you can reconstruct the dictionary structure from that.

Check if an object exist in a NSMutableArray of NSDictionary

i have an NSMutableArray that contains some NsDictionary with NSstring for differents Keys.
Somethings like:
NSDictionary *ExempleDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[Date objectAtIndex:sender.tag],#"DATE",[Time objectAtIndex:sender.tag],#"TIME", nil];
I'd like to check if a particular object with DATE=xxx && TIME=xxx exist in the Array.
Any idea?!
You can do the search with NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.DATE=%# AND SELF.TIME=%#", dateVal, timeVal];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];
filteredArray contains an NSArray of all NSDictionary objects matching the specified date and time condition. You can retrieve the matching objects by iterating the filtered array.
You can use NSArray's containsObject: (if you have a reference to the dictionary laying around) or indexOfObjectIdenticalTo: (if you want to check if an element identical to the one you are passing -but not necessarily the same object- is in the array).
Both are explained in the docs:
containsObject
indexOfObjectIdenticalTo

How to compare and remove common objects( NSDictionaries) from 2 NSMutableArray?

I have two NSMutableArrays, filled with dictionary objects, like
NSMutableArray * bigArray = #[dictionary1,dictionary2,dictionary3];
NSMutableArray * smallArray = #[dictionary1];
I want to remove the common elements (dictionay1) from the big array
Note: small array is subset of big array and elements are distinct
[bigArray removeObjectsInArray:smallArray];
removes all objects in bigArray that are contained in smallArray.
If each element is distinct (as you say in the comments) then you can filter the array using a predicate that removes all the objects that inside of the smaller subset. Since you are using an array in your question, I assume that the order of the objects matter.
If you need to preserve the order, then you will need to filter the array. Converting to a set will break the order (unless you are able to resort it afterwards.
You are using mutable arrays in your question, so I will do the same. Just be aware that the original array is actually modified:
NSMutableArray *original = [NSMutableArray arrayWithArray:#[#"A", #"E", #"C", #"B", #"D"]];
NSMutableArray *subset = [NSMutableArray arrayWithArray:#[#"C", #"B"]];
NSLog(#"before : %#", original);
[original filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
// return YES for objects to keep
return ![subset containsObject:evaluatedObject];
}]];
NSLog(#"after : %#", original);
The output of this code is:
before : (
A,
E,
C,
B,
D
)
after : (
A,
E,
D
)
You can see that the order is preserved.
If you don't want to modify the original array you can produce a new filtered array instead. The small difference is that you call filteredArrayUsingPredicate: instead of filterUsingPredicate:. The predicate is the same:
NSArray *filtered = [original filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
// return YES for objects to keep
return ![subset containsObject:evaluatedObject];
}]];
NSSet is perfect class to do this kind of job. Try that;
NSMutableSet *bigSet = [NSMutableSet setWithArray:bigArray];
[bigSet minusSet:[NSMutableSet setWithArray:smallArray]];
bigArray = [[bigSet allObjects] mutableCopy];
The thing worth to point is that if your array will store duplicate and you convert it to NSSet the duplicate will be removed in that case you shouldn't use it. The second thing is it can change order of the elements in your array.

Getting a NSDictionary that has a particular key inside an array

I have an NSArray of NSDictionaries.
The dictionaries has keys like this: color, number, code, description and size.
Now I have a key and I want to locate that dictionary inside the array, some magic command like:
NSDictionary *oneDict = [get a dictionary from myArray where "code" is equal to "32"];
code is a key, 32 a value.
I know how to enumerate the array and search one by one every dict, but I know objective-c is a bag full of "tricks" and a "magic command" may exist to extract surgically this dictionary from the array in one line.
Any clues?
You can use indexOfObjectPassingTest to get the object index:
NSUInteger index = [myArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary* dict = obj;
return [obj[#"code"] intValue] == 32;
}];
If not found, result will be NSNotFound;
This can be done with an NSPredicate simpler:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"code MATCHES[cd] %#", value];
NSArray *result = [myArray filteredArrayUsingPredicate:predicate];
The result array will hold all NSDictionaries that did match the value.
Take a look at the NSPredicate documentation
It is very powerful
If you intend to do the lookup many times and don't mind spending some time on setup, you could create a dictionary that maps the values of code to the dictionaries that contain those values. i.e.:
NSDictionary * mapping = [ NSDictionary dictionaryWithObjects:myArray forKeys:[ myArray valueForKey:#"code" ] ]
id answer = mapping[#"32" ] ;
This only works if code contains unique values. If the values are not unique:
NSArray * codeValues = [ myArray valueForKey:#"code" ] ;
NSIndexSet indexes = [ codeValues indexesOfObjectsPassingTest:^BOOL(id object){ [ object isEqual:#"32" ] } ] ;
NSArray * matchingDictionaries = [ myArray objectsAtIndexes:indexes ] ;
matchingDictionaries will be an array containing dictionaries where code is #"32".
Replace #"32" in these examples with the value of code you wish to find, &c.

How would I use an NSMutableArray with a NSMutableDictionary

I have an NSMutableDictionary of websites
[dictionaryOfSites setObject:#"http://www.example.com" forKey:#"Example.com"];
[dictionaryOfSites setObject:#"http://www.site1.com" forKey:#"Site1"];
[dictionaryOfSites setObject:#"http://www.apple.com" forKey:#"Apple"];
I know you can't sort a dictionary. But I've read that other people have used an NSMutableArray as the key and the array can be sorted.
So if I setup a new array
[[arrayKey alloc] initWithObjects:#"Example.com", #"Site1", #"Apple", nil];
I would then modify my first snippet to
[dictionaryOfSites setObject:#"http://www.example.com" forKey:[arrayForKey objectAtIndex:0]];
[dictionaryOfSites setObject:#"http://www.site1.com" forKey:[arrayForKey objectAtIndex:1]];
[dictionaryOfSites setObject:#"http://www.apple.com" forKey:[arrayForKey objectAtIndex:2]];
In this simple problem, I had 3 sites so I "hard" coded it. How would I do the same thing if my list of sites was 100? How would the order of the sites be maintained?
If I sort my array
[arrayKey sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
Wouldn't index 2 become index 0? If it becomes index 0 then you can see the dictionaryOfSites has the wrong label with the URL.
So you can use a custom class (as I mentioned above in my comment), or better yet use an NSDictionary to store the values as MarkM suggested.
EDIT: "i don't have to maintain a dictionary. its a new app from the ground up."
Since you don't need to start with one big dictionary like you posted, it would be better to just store individual dictionary objects for each site in an array and not have to worry about the conversion.
// Setup the initial array
NSMutableArray *arrayOfSites = [NSMutableArray new];
[arrayOfSites addObject:#{#"Name" : #"Example.com",
#"URL" : #"http://www.example.com"}];
[arrayOfSites addObject:#{#"Name" : #"Site1",
#"URL" : #"http://www.site1.com"}];
[arrayOfSites addObject:#{#"Name" : #"Apple",
#"URL" : #"http://www.apple.com"}];
// At this point, arrayOfSites contains a dictionary object for each site.
// Each dictionary contains two keys: Name and URL with the appropriate objects.
// Now we just need to sort the array by the Name key in the dictionaries:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
[arrayOfSites sortUsingDescriptors:[NSArray arrayWithObjects:descriptor, nil]];
NSLog(#"%#", arrayOfSites);
Results:
2013-05-07 18:19:08.386 Testing App[75712:11f03] (
{
Name = Apple;
URL = "http://www.apple.com";
},
{
Name = "Example.com";
URL = "http://www.example.com";
},
{
Name = Site1;
URL = "http://www.site1.com";
} )
To access the data, you would use:
NSString *name = [[arrayOfSites objectAtIndex:indexPath.row] objectForKey:#"Name"];
Note that arrayOfSites should be a declared property of your class so that you can access it from different methods.
What you need to do is store your NSDictionary objects in the array and then access a value from that array to do the sorting if you wish. You don't actually store a new string for the sorting. You just check the value of a certain key in the dictionary at the index in the array.
Here is a good source for sorting an array of dictionaries

Resources