Application crashed in enumerateObjectsUsingBlock - ios

My application crashed at following codes:
NSMutableArray* array = [dict objectForKey:key];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];
And the crash information is:
1 CoreFoundation 0x2ecd30f2 __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke (in CoreFoundation) + 90
2 CoreFoundation 0x2ecd3024 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] (in CoreFoundation) + 232
I can't figure it out, anyone knows how to solve this problem?

try to use:
[word isEqualToString:#"jerry"]

Try this:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
stop = YES;
find = YES;
}
You are comparing strings in the wrong way.

Try this
NSMutableArray* array = [NSMutableArray arrayWithArray:[dict objectForKey:key]];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];

My money is on your array turning out to not actually be an array. When you create and set your array from the object in dict, check to see that [dict objectForKey:key] is actually an array.
NSMutableArray* array = [dict objectForKey:key];
NSLog(#"class of array: %#", [[dict objectForKey:key] class]);

Related

short cut technique for finding null value from Dictionary?

I have 100 key and value in nsmutabledictornary and i want to check that any value have null or not. Do you have any short function or technique?
I don't want to multiple line code like check every key and value. Your answer would be appreciated.
This code will give you the set of keys which have (non)null values. You can't store actual nil values in a dictionary, so [NSNull null] is assumed. The predicate is trivially alterable to any other condition.
NSDictionary *d = #{ #"a" : #"1", #"b" : [NSNull null] };
NSSet *nullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]];
}];
NSSet *nonnullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]] == NO;
}];
From here, you can use the keys to generate a corresponding dictionary, if needed.
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([nonnullKeys contains:key]) {
nonNullDict[key] = obj;
}
}];
If you don't need a separate list of keys, and just need the filtered dictionary, skip the first step and modify the second part to read as follows:
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSNull null]] == NO) {
nonNullDict[key] = obj;
}
}];
Write category on NSDictionary it will provide you null free dictionary. Here is the category I have written for myself.
code for .h file (interface)
#import <Foundation/Foundation.h>
#interface NSDictionary (CheckNull)
{
}
- (NSDictionary *)nullFreeDictionary;
#end
Code for .m file. (implementation)
#import "NSDictionary+CheckNull.h"
#implementation NSDictionary (CheckNull)
- (NSDictionary *) nullFreeDictionary
{
NSMutableDictionary *tempDictionary = [self mutableCopy];
for (NSString *key in tempDictionary.allKeys) {
NSString *value = [tempDictionary valueForKey:key];
if ([value isKindOfClass:[NSString class]]) {
if (value == (id)[NSNull null] || value == nil || value.length == 0) {
[tempDictionary setValue:#"" forKey:key];
}
}
}
return tempDictionary;
}
Call null free method on your dictionary using above category.
NSDictionary *dict = [dict nullFreeDictionary];
//To remove NULL from Dictionary
-(NSMutableDictionary *)removeNullFromDictionary : (NSMutableDictionary *)dict
{
// if (![dict isKindOfClass:[NSMutableDictionary class]])
// {
// }
dict = [[NSMutableDictionary alloc] initWithDictionary:dict];
for (NSString * key in [dict allKeys])
{
if ([dict[key] isKindOfClass:[NSNull class]])
{
[dict setValue:#"" forKey:key];
}
else if ([dict[key] isKindOfClass:[NSMutableDictionary class]]||[dict[key] isKindOfClass:[NSDictionary class]])
{
dict[key] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:dict[key]]];
}
else if ([dict[key] isKindOfClass:[NSMutableArray class]]||[dict[key] isKindOfClass:[NSArray class]])
{
dict[key] = [self removeNullFromArray:[NSMutableArray arrayWithArray:dict[key]]];
}
}
return dict;
}
//To remove NULL from Array
-(NSMutableArray *)removeNullFromArray : (NSMutableArray *)arr
{
// if (![arr respondsToSelector:#selector(addObject:)])
// {
// arr = [[NSMutableArray alloc] initWithArray:arr];
// }
arr = [[NSMutableArray alloc] initWithArray:arr];
for (int cnt = 0; cnt<[arr count]; cnt++)
{
if ([arr[cnt] isKindOfClass:[NSNull class]])
{
arr[cnt] = #"";
}
else if ([arr[cnt] isKindOfClass:[NSMutableDictionary class]]||[arr[cnt] isKindOfClass:[NSDictionary class]])
{
arr[cnt] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:arr[cnt]]];
}
else if ([arr[cnt] isKindOfClass:[NSMutableArray class]]||[arr[cnt] isKindOfClass:[NSArray class]])
{
arr[cnt] = [self removeNullFromArray:[NSMutableArray arrayWithArray:arr[cnt]]];
}
}
return arr;
}

iPhone Get Index of an array from Value in a Key

I have the next NSMutableArray:
Product[0] {
name => val1,
Price => pricevalue 1
}
Product[1] {
name => val2,
Price => pricevalue2
}
I want to search for: name = val2 and return the index of product, so 1.
Use like this
NSIndexSet *indices = [array
indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[obj objectForKey:#"name"] isEqualToString:#"val2"];
}];
Happy coding.........
__block NSUInteger index = NSUIntegerMax;
[products enumerateObjectsUsingBlock: ^ (Product* product, NSUInteger idx, BOOL* stop) {
if([product.name isEqualToString:#"val2"])
{
index = idx;
*stop = YES;
}
}];
Edit: Ravindra's solution is more elegant!

Remove object from NSMutableArray?

I have an array with following elements in ViewDidLoad method
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
I have another UITextField for searching the elements .So once i type some thing in UITextField i want to check whether that string is present in "inputArray" or not.If it is not matching with elements in inputArray then remove the corresponding elements from inputArray .
for (NSString* item in inputArray)
{
if ([item rangeOfString:s].location == NSNotFound)
{
[inputArray removeObjectIdenticalTo:item];//--> Shows Exception
NSLog(#"Contains :%#",containsAnother);
}
}
but this code shows exception , something related to "removeobject:"
Exception :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument'
*** First throw call stack:
`
In fast enumeration you can NOT modify the collection.
The enumerator object becomes constant and immutable.
If you want to do updation on the array
You should like this :
NSMutableArray *inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
NSString *s=#"bus";
for (int i=inputArray.count-1; i>-1; i--) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
}
}
EDIT:
The above works similar as this :
NSArray *array=[inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF CONTAINS[c] %#",s]];
You can use the following code
for (int i=0;i<[inputArray count]; i++) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
i--;
}
}
That needs to be an NSMutableArray. You can't modify an NSArray once created (except to start all over).
Change this:
inputArray = [NSArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
to this:
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
and also change the property to NSMutableArray also:
#property(nonatomic, strong) NSMutableArray *inputArray;
The s in your question is probably nil. So your are getting the exception.Please check that out.
Use following Code. (This Code is use for filter Array base on input string/text of UITextField )
Take Two NSMutableArray and add one array to another array in ViewDidLoad method such like,
self.listOfTemArray = [[NSMutableArray alloc] init]; // array no - 1
self.ItemOfMainArray = [[NSMutableArray alloc] initWithObjects:#"YorArrayList", nil]; // array no - 2
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray]; // add 2array to 1 array
And Write following delegate Method of UISearchBar
- (BOOL) textFieldDidChange:(UITextField *)textField
{
NSString *name = #"";
NSString *firstLetter = #"";
if (self.listOfTemArray.count > 0)
[self.listOfTemArray removeAllObjects];
if ([searchText length] > 0)
{
for (int i = 0; i < [self.ItemOfMainArray count] ; i = i+1)
{
name = [self.ItemOfMainArray objectAtIndex:i];
if (name.length >= searchText.length)
{
firstLetter = [name substringWithRange:NSMakeRange(0, [searchText length])];
//NSLog(#"%#",firstLetter);
if( [firstLetter caseInsensitiveCompare:searchText] == NSOrderedSame )
{
// strings are equal except for possibly case
[self.listOfTemArray addObject: [self.ItemOfMainArray objectAtIndex:i]];
NSLog(#"=========> %#",self.listOfTemArray);
}
}
}
}
else
{
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray ];
}
[self.tblView reloadData];
}
}
Output Show in your Console.
As others have said you can't mutate an array while it is being enumerated. The easiest way to do what you want and keep the convenience of fast enumeration is to copy the array.
for (NSString* item in [inputArray copy]) {
...
}
This is a clean solution that I like to use. You define a NSArray category to extend it and create a map method. This method creates a new NSArray based on what you return within your block:
#interface NSArray (BlockExtensions)
/*!
Invokes block once for each element of self, returning a new array containing the
values returned by the block.
*/
- (NSArray *)map:(id (^)(id obj))block;
#end
#implementation NSArray (BlockExtensions)
- (NSArray *)map:(id (^)(id obj))block
{
return [self mapWithOptions:0 usingBlock:^id(id obj, NSUInteger idx) {
return block(obj);
}];
}
- (NSArray *)mapWithOptions:(NSEnumerationOptions)options usingBlock:(id (^)(id obj, NSUInteger idx))block
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
[self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id newobj = block? block(obj, idx) : obj;
if (newobj)
[array addObject:newobj];
}];
return array;
}
#end
The block will be called once for every item in your original array, passing this object as a parameter:
NSArray *newArray = [inputArray map:^id(NSString *item) {
if ([item rangeOfString:s].location == NSNotFound) {
return item;
}
return nil;
}];
newArray will contain your filtered out items!
+1 to Anoop for pointing out that you can use filteredArrayUsingPredicate. Thus, if you wanted to create a new array based upon the matches in inputArray, you could also use something like:
NSArray *matchingArray = [inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Alternatively, given that inputArray is a NSMutableArray you can simply filter the array with this single line:
[inputArray filterUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Or, if you like blocks:
[inputArray filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject rangeOfString:s].location != NSNotFound);
}]];

Enumerate NSArray starting at center / middle of array searching both ways

How can I enumerate an array starting at the center of the array?
#implementation NSArray (Extensions)
- (void)enumerateFromCenterGoBothWaysUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
{
NSMutableArray *copy = [self mutableCopy];
BOOL shouldStop = NO;
while([copy count] > 0 && shouldStop == NO)
{
NSUInteger index = [copy count] / 2;
id obj = copy[index];
[copy removeObject:obj];
block(obj, index, &shouldStop);
}
}
#end

Need to modify an NSMutableArray that is pre-loaded with data during fast enumeration in iOS

I have an NSMutableArray that is holding a list of objects. What I am trying to do is iterate through this list of objects, and find the matching object for the I am trying to insert. Once I find the matching object, I then want to simply replace the object that is currently in the list with the one I am trying to insert. I am trying to do this using fast enumeration:
TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];
for (TestResult *checkTest in [DataModel sharedInstance].testResultList) {
NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
return [obj.name isEqualToString:name];
}];
if (indx != NSNotFound) {
[[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}
}
Unfortunately, when I run the above code, I am getting the following error:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x9624820> was mutated while being enumerated.'
Can anyone see what I am doing wrong, and how can work around this, yet achieve the functionality that I described above?
First of all the crash is pretty self explanatory.
You are actually mutating (aka. replacing an object in the array), while the fast enumeration is still in progress, which is not permitted.
The solution if you would go with your design would be to actually capture the index of the object, break the fast enumeration and replace the object outside the fast enumeration.
However what you are doing is not correct.
The way to use the indexOfObjectPassingTest is this:
NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
return [obj.name isEqualToString:name];
}];
if (indx != NSNotFound) {
[[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}
You don't need to manually enumerate through all elements of the array.
The function does this for you internally.
As the error message say you can't modify the array used in a foreach bucle. Change your code to this:
TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];
NSInteger indx = NSNotFound;
for (TestResult *checkTest in [DataModel sharedInstance].testResultList) {
indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
return [obj.name isEqualToString:name];
}];
}
if (indx != NSNotFound) {
[[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}
You cannot replace, delete or insert elements in an array while being enumerated. But you could change all the attributes of the element at idxn to the attributes of the result. This way the objects remain.
Hope this helps!
As the error message says, you cannot edit an array which is being enumerated. You need to have a different array and then you can do this.
The following would have worked,
TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];
NSArray *array = [NSArray arrayWithArray:[DataModel sharedInstance].testResultList];
for (TestResult *checkTest in array) {
NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
return [obj.name isEqualToString:name];
}];
if (indx != NSNotFound) {
[[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}
}
Update:
As per the comment from Lefteris, you dont need to iterate through the array if you are using indexOfObjectPassingTest method.
You can simply use,
TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];
NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
return [obj.name isEqualToString:name];
}];
if (indx != NSNotFound) {
[[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}

Resources