Dictionary allKeys vs keyEnumerator? - ios

Which to use when?
What if I am looping the dictionary and removing key-values from it, will the enumerator work for that? Documentation seem to answered that question:
If you use this method with instances of mutable subclasses of
NSDictionary, your code should not modify the entries during
enumeration. If you intend to modify the entries, use the allKeys
property to create a “snapshot” of the dictionary’s keys. Then use
this snapshot to traverse the entries, modifying them along the way.
Performance?, because people always want to know.

Iteration through NSDictionary could be achieved at least in two ways: using NSArray with [NSDictionary allKeys] or NSEnumerator.
NSArray *keyArray =  [bigUglyDictionary allKeys];
int count = [keyArray count];
for (int i=0; i < count; i++) {
  NSDictionary *tmp = [bigUglyDictionary objectForKey:[ keyArray objectAtIndex:i]];
}
NSEnumerator *enumerator = [bigUglyDictionary keyEnumerator];
id key;
while ((key = [enumerator nextObject])) {
NSDictionary *tmp = [bigUglyDictionary objectForKey:key];
}
Second way is a little bit faster, so if you work with huge dictionaries and have no need of array with their keys

Related

Looping thru NSArray of NSString logic

I need help with the following:
I have an NSArray with NSStrings, I want to loop thru these strings and find a matching string, when match is found the strings after this match will be extracted into an NSDictionary until a certain other match is hit.
Here is an example:
NSArray *array = #[#"Fruit",#"Apple",#"Vegtable",#"Tomato",#"Fruit",#"Banana",#"Vegtable",#"Cucumber"];
So I want to loop thru this array and split it in 2 arrays one for fruit and one for vegetable.
Anyone can help with the logic?
Thanks
This is probably the simplest way to solve the problem:
NSArray *array = #[#"Chair",#"Fruit",#"Apple",#"Orange",#"Vegetable",#"Tomato",#"Fruit",#"Banana",#"Vegetable",#"Cucumber"];
NSMutableArray *fruitArray = [NSMutableArray array];
NSMutableArray *vegetableArray = [NSMutableArray array];
NSMutableArray *currentTarget = nil;
for (NSString *item in array)
{
if ([item isEqualToString: #"Fruit"])
{
currentTarget = fruitArray;
}
else if ([item isEqualToString: #"Vegetable"])
{
currentTarget = vegetableArray;
}
else
{
[currentTarget addObject: item];
}
}
In one iteration over the array, you just keep adding items to a result array using a pointer to one of two result arrays according to the last occurrence of the #"Fruit" or #"Vegetable" string.
This algorithm ignores all items before the first occurrence of the #"Fruit" or #"Vegetable" string, because the currentTarget is initialized to nil, which ignores the addObject: messages. If you want different behaviour, just change the initialization.
You said you wanted the results in a NSDictionary, but didn't specify what should be the key. If you want one NSDictionary with two keys, Fruit and Vegetable, and values NSArrays containing the items, just use the arrays previously created:
NSDictionary *dict = #{ #"Fruit": fruitArray, #"Vegetable": vegetableArray };
PS: You have a typo in your example, Vegtable instead of Vegetable. I corrected it in my code, so keep it in mind.
If I completely understand you:
NSArray *array = #[#"Fruit",#"Apple",#"Vegtable",#"Tomato",#"Fruit",#"Banana",#"Vegtable",#"Cucumber"];
NSMutableArray *fruits = [NSMutableArray array];
NSMutableArray *vegtables = [NSMutableArray array];
for (NSInteger i = 0; i < array.count; ++i){
if ([array[i] isEqualToString:#"Fruit"]){
++i;
[fruits addObject:array[i]];
}
else if ([array[i] isEqualToString:#"Vegtable"]){
++i;
[vegtables addObject:array[i]];
}
}

a faster way to mutate an Array of NSMutableDictionaries

based on the fact that you cannot edit mutable Collections while enumerating them, this is the best solution i could come up with to edit a Array of NSMutableDictionaries:
__block NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithCapacity:1];
__block NSUInteger idx;
[_myArray enumerateObjectsUsingBlock:^(NSMutableDictionary* obj,
NSUInteger indx, BOOL *stop) {
if (// some condition is met) {
tempDict = [obj mutableCopy];
idx = indx;
}
}];
[tempDict setObject:[NSNumber numberWithInt:thisQueryResults] forKey:#"resultsNum"];
[_myArray replaceObjectAtIndex:idx withObject:rowSelected];
this seems way too complicated (even for a language like obj-c).. and since it's involving two data types (NSMutableArray and NSMutableDictionary), it doesn't seem like I can cleanly put them into a category.. advice?
update: one comment asked why do I create a mutablecopy (as opposed to just a copy.. since it's copying a mutable object)..
suppose I just used copy.. if i put a break on tempDict this is what I get:
// tempDict = [obj copy]
po tempDict
$0 = 0x0b28cc10 <__NSArrayI 0xb28cc10>(
1
)
// tempDict = [obj mutableCopy]
po tempDict
$0 = 0x0b28cc10 <__NSArrayM 0xb28cc10>( //notice the M in __NSArrayM as opposed to I above
1
)
in case of copy.. if I follow it with a line like this:
[tempDict setObject:[NSNumber numberWithInt:thisQueryResults] forKey:#"resultsNum"];
I get this error:
[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0xb245100
I get the same above error with this code:
for (NSUInteger idx = 0; idx < [_myMutableArray count]; idx++) {
NSMutableDictionary* myMutableDict = _myMutableArray[idx];
[myMutableDict setObject:obj forKey:key];
}
update 2:
the origin of the problem was instantiating non mutable arrays and dictionaries.. I'm new to the whole new obj-c literals, so I didn't know that to create a NSMutableArray and NSDictionary, you gotta do this, respectively:
[#[..] mutableCopy]
[#{..} mutableCopy]
So in your case, I don't quite follow why you call tempDict = [obj mutableCopy]; when from the conditions you write the dictionary is already writable.
You can use several tricks. Like using
for (NSUInteger idx = 0; idx < _myArray.count: idx++_ {
NSMutableDictionary *obj = _myArray[idx];
// modify
}
For NSDictionaries you can get allKeys and iterate over that copy. This is a bit slower than using fast enumeration, but still faster than doing workarounds like boxing integers to replace later :)
In your case you are NOT modifying the array at all only the dictionaries within the array. There are no contstraits on how you modify the objects within the array. Here is a bit of equivalent code:
for (NSMutableDictionary *dict in _myArray) {
if (someCondition)
[dict setObject:[NSNumber numberWithInt:thisQueryResults] forKey:#"resultsNum"]
}
You would have a problem if you absolutely needed to replace the object in your array. In that case, if the array is not huge I would suggest the same as #Markus. Iterate over a copy and modify the original.
Maybe you can use KVC and do :
NSArray<NSMutableDictionary *> *result = [[_myArray filteredArrayUsingPredicate:[NSPredicate withFormat:#"{YOUR CONDITION}"]] valueForKey:#"mutableCopy"];

How can I move the values in NSMutableArray to NSArray

I have an array of recommendedcar IDs, and another array of allcar IDs. From this, I have to take the recommendedcar images. First, I check whether the recommended carid is in my allcar id; if it is, I select the corresponding car images, and store them into NSArray.
This is the code I am using.
for (int i=0;i<[listOfCarId count];i++) {
for (int j=0;j<[_allCarID count];j++) {
tempAllCarId=[_allCarID objectAtIndex:j];
tempRecommendedCarId=[listOfCarId objectAtIndex:i];
if ([tempRecommendedCarId isEqualToString:tempAllCarId]) {
_recommendedCarImage=[_allCarImages objectAtIndex:j];
NSLog(#"finalImage%#",_recommendedCarImage);
}
}
}
_recommendedcarImage is NSMUtableArray; I want a NSArray. How can I convert it to a NSArray?
How can i replace the "_recommendedCarImage " with an NSArray?? Currently _recommendedCarImage is a mutable array.
Polymorphism. Since NSMutableArray is a subclass of NSArray, you can use it anywhere an NSArray is expected. You don't have to do anything.
Now its working,What i did is , I just copy the contents of Mutable array to NSarray
recommendedArray=[_recommendedCarImage copy];
An NSMutableArray is an NSArray already (as it's a subclass of NSArray), still you can do:
NSArray *array = [NSArray arrayWithArray:mutableArray];

NSMutableArray with NSDictionary to NSMutableDictionary

Here is the situation:
I have a request on AFNetworking that retrieves me a JSON with an NSArray.
My goal is to mutate the NSDictionaries inside it. I already made a mutableCopy of the array, but I want to know if I can easily mutate all the content. Will I have to iterate through the array manually?
NSJSONSerialization has options to allow you to control the mutability of the resulting data structure. Just pass the appropriate ones (probably NSJSONReadingMutableContainers) and there you go.
You cannot mutate NSDictionary, just because only NSMutableDictionary has method setObject:forKey:
So you should create mutableCopy of each dictionary and empty mutable array. Then with a forloop fill that array. Your code should be so:
- (NSMutableArray *)mutatedArrayFromArray:(NSArray *)array
{
NSMutableArray *resultArray = [NSMutableArray new];
if([array count] > 0)
{
for(int i = 0; i < count; i++)
{
NSMutableDictionary *mutatedItem = [[array objectAtIndex:i] mutableCopy];
[resultArray addObject:mutatedItem];
[mutatedItem release]; // only with ARC disabled
}
}
return [result autorelease]; // if ARC enabled : return result;
}

Dynamica string count in NSMutableArray in NSMutableDictionary

I am familiar with getting a string count from a known array
int numberOfWords = [self.wordArray count];
but I have an unknown number of strings in an unknown number of arrays, all referenced by a dictionary. This works - good.
NSMutableDictionary *eqClasses = [[NSMutableDictionary alloc] init];
The arrays and strings are added at runtime (with help of this board):
NSMutableArray* array = [eqClasses objectForKey:wordPattern];
if(!array) {
// create new array and add to dictionary if wordPattern not found
array = [NSMutableArray array];
[eqClasses setObject:array forKey:wordPattern];
}
[array addObject:tempWordStr];
Now I need to iterate through the dictionary and get the array with the largest word count. Is there a way to scroll through all the arrays in the dictionary without using a key (I won't know all the word patterns as they are generated dynamically), AND once I find the array with the most words, get that array/value and key/wordpattern?
Well, there is a way to get all the keys within a dictionary:
NSArray *keyArray = [myDict allKeys];
And then you just go through the array and get the object for each key.
A fast enumeration should work nicely.
for (NSString *string in NSArray){
...
} //Assuming your keys are strings!
You can save each string to a temporary string, and when encountering a new string, compare to find the longer one. If it's longer, replace the old string with the longer one.
Hope this helped! ^_^
^_^
Okay, so now that you have an array full of all the keys in the dictionary,
you can iterate through the entire array and get the corresponding value (the string) for each key.
NSArray *keyArray = [myDict allKeys]; //This gets all the keys
NSString *tempString = #""; //This is the string you will save the longest string in. It gets updated when a longer string is found in the following loop.
for (NSString *string in keyArray){
NSString *stringFromCurrentKey = [myDict objectForKey:string];
if(stringFromCurrentKey.length > tempString.length){
tempString = stringFromCurrentKey;
}
} //By the end, you should be left with the longest string contained in tempString!
^_^ Hope this made sense and helped!
Try this code:
NSArray *largestArray = nil;
for (NSString *key in dictionary)
{
NSArray *array = [dictionary objectForKey:key];
if (array.count > largestArray.count) // largestArray.count is 0 if largestArray is nil
{
largestArray = array;
}
}

Resources