Remove object from NSMutableArray? - ios

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

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

Removing Duplicate Custom Objects from two arrays and combining them

First off i have searched a lot but all methods seems to be for primitives or for whole custom objects.
My situation is this. I have a type custom objects in two different arrays. However the fields of every single objects is quite different to another with the exception of only 2 fields.
What i want is combine both of these arrays and then remove duplicates with respect to only those two fields.How can i do that. My Code so far
NSMutableArray* testArray = [eventHandler returnAllEvents];
NSMutableArray* combinedArray = [[NSMutableArray alloc]init];
NSArray* finalArray = [[NSArray alloc]init];
if (testArray.count==0) {
for (int i = 0; i<facebookData.count; i++) {
LSEvent* event = [facebookData objectAtIndex:i];
[combinedArray addObject:event];
}
finalArray = [combinedArray arrayByAddingObjectsFromArray:calendarData];
}
NSMutableArray *uniqueArray = [NSMutableArray array];
NSMutableSet *names = [NSMutableSet set];
for (id obj in finalArray) {
NSString *destinationName = [obj destinationname];
if (![names containsObject:destinationName]) {
[uniqueArray addObject:obj];
[names addObject:destinationName];
}
}
You can do sth like this
NSArray first = ...
NSMutableArray second = ... // this will be combine array
for (id someObj in first) {
if ( [second filteredArrayUsingPredicate:[self predicateForObject:someObj ]].count == 0 ){
[second addObject: someObj];
}
}
If you want to check that object exists in array using containsObject: you need to implement - (BOOL)isEqual:(id)other in your custom object.
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
}
if (!other || ![other isKindOfClass:[self class]]) {
return NO;
}
if (self.identifier == other.identifier) {
return NO;
}
return YES;
}

Remove duplicates from NSArray case insensitively using NSSet

NSArray*arr = #[#"ram",#"Ram",#"vinoth",#"kiran",#"kiran"];
NSSet* uniqueName = [[NSSet alloc]initWithArray:arr];
NSLog(#"Unique Names :%#",uniqueName);
Output:
but i need the output as
You could first convert them all to lowercase strings.
NSArray *arr = #[#"ram",#"Ram",#"vinoth",#"kiran",#"kiran"];
NSArray *lowerCaseArr = [arr valueForKey:#"lowercaseString"];
NSSet* uniqueName = [[NSSet alloc] initWithArray:lowerCaseArr];
NSLog(#"Unique Names :%#",uniqueName);
Unique Names :{(
ram,
kiran,
vinoth
)}
Try this:
NSArray *arr = [NSArray arrayWithObjects:#"Ram",#"ram", nil]; //this is your array
NSMutableArray *arr1 = [[NSMutableArray alloc]init]; //make a nsmutableArray
for (int i = 0; i<[arr count]; i++) {
[arr1 addObject:[[arr objectAtIndex:i]lowercaseString]];
}
NSSet *set = [NSSet setWithArray:(NSArray*)arr1];//this set has unique values
This will always preserve casing form that was existing in your original container (although it's undefined which casing):
NSArray<NSString*>* input = ...
NSMutableDictionary* tmp = [[NSMutableDictionary alloc] init];
for (NSString* s in input) {
[tmp setObject:s forKey:[s lowercaseString]];
}
return [tmp allValues];
Create a mutable array the same size as arr. Fill it with lowercaseString versions of each element of arr. Make the set out of that.
#Updated
Using this you remove uppercase string from your array.
NSMutableArray *arr= [[NSMutableArray alloc]initWithObjects:#"ram",#"Ram",#"vinoth",#"kiran", nil];
NSMutableArray *arrCopy = [[NSMutableArray alloc]init];
for (int index = 0 ; index<arr.count; index++) {
NSUInteger count = [[[[arr objectAtIndex:index] componentsSeparatedByCharactersInSet:[[NSCharacterSet uppercaseLetterCharacterSet] invertedSet]] componentsJoinedByString:#""] length];
if (count == 0) {
[arrCopy addObject:[arr objectAtIndex:index]];
}
}
NSLog(#"Print Mutable Copy %#",arrCopy);
try this one
NSArray *copyArray = [mainArray copy];
NSInteger index = [copyArray count] - 1;
for (id object in [copyArray reverseObjectEnumerator]) {
if ([mainArray indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound) {
[mainArray removeObjectAtIndex:index];
}
index--;
}
copyArray=nil;

NSMutableArray (1 dim) from NSArray (2dim)

I want to make 1 array that will hold all of the other arrays objects and will look like this
("052-6224754","03-6475075","02-6753231")
my code is:
-(NSMutableArray*) getRecepientsPhones
{
NSMutableArray* phones = [[NSMutableArray alloc]init];
//scroll all choosed contacts and retrieve phones to nsstring
if([recepientsFromContacts count]>0)
for (int i=0; i<[recepientsFromContacts count]; i++)
{
NSMutableArray* tempArray = [[NSMutableArray alloc]init];
if(![[[recepientsFromContacts objectAtIndex:i]objectForKey:#"CPhones"]isKindOfClass:[NSNull class]])
{
[tempArray addObject:[[recepientsFromContacts objectAtIndex:i]objectForKey:#"CPhones"]];
for(int j = 0; j<[tempArray count];j++)
{
[phones addObject:[tempArray objectAtIndex:j]];
}
}
}
//lets fetch from that contact
if([personRecepient count]>0)
{
if(![[personRecepient objectForKey:#"CellPhone"]isKindOfClass:[NSNull class]])
[phones addObject:[personRecepient objectForKey:#"CellPhone"]];
}
NSLog(#"%#",phones);
return phones;
}
[[recepientsFromContacts objectAtIndex:i]objectForKey:#"CPhones"]
is 1 or more dimension array (it is array of phone numbers per person , person can have more than 1 number)
example: ("052-6224754","03-6475075")
but my function returns
("052-6224754","03-6475075"),("02-6753231")
which is not good , what should I do to make it 1 array
("052-6224754","03-6475075","02-6753231")
You should change the line
[phones addObject:[tempArray objectAtIndex:j]];
to
[phones addObjectsFromArray:[tempArray objectAtIndex:j]];
This should result in a flattened array of phone numbers.
Then you should head over to codereview.stackexchange.com because there are several issues with your code fragment.
Edit: Here's a cleaned up version of the method:
- (NSArray *)recepientsPhoneNumbers
{
NSMutableArray* phoneNumbers = [NSMutableArray array];
for (NSDictionary *dict in _recepientsFromContacts)
{
id recipientPhoneNumbers = dict[#"CPhones"];
if (recipientPhoneNumbers != [NSNull null])
[phoneNumbers addObjectsFromArray:recipientPhoneNumbers];
}
id recipientPhoneNumbers = _personRecepient[#"CellPhone"];
if (recipientPhoneNumbers != [NSNull null])
[phoneNumbers addObjectsFromArray:recipientPhoneNumbers];
NSLog(#"%#", phoneNumbers);
return phoneNumbers;
}
I applied Cocoa coding conventions, so ivars are now prefixed with underscores.

Display cell section wise crashes iOS app

I have Tableview with sections in it from A to Z (no of sections are not fixed i calculated dynamically)
I want to display like this:
:
My array values: msg_array=["AajKaCatch","AajKaItem","Anari","Big C Mobiles","Big Flix","BigRock","caksonflowers, ...."]
when i try to display like this in cellForRowAtIndexPath it shows NSInvalidArgumentException
cell.textLabel.text=[[[msg_array objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] objectForKey:#"Merchant_Name"];
please help and Thanks In advance.
Your array is like:
array{object,object,object,object,object};
In such a situation, you can't use like:
[[msg_array objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]
Because for implementing such one, the [msg_array objectAtIndex:indexPath.section] should return an array.
So implementing this, you need to try like this:
array{array{objects starts with 'A'},array{objects starts with 'B'},array{objects starts with 'C'}};
When you are doing this:
[[[msg_array objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] objectForKey:#"Merchant_Name"];
You are accessing an element of msg_array as if it was a NSArray, that contains a NSDictionary.
But, inside of msg_array you just have NSStrings.
The structure you are triying to access is:
NSArray -> NSArray -> NSDictionary
And you have
NSArray -> NSString
I have done the same thing for contact info and other things like that using FKRSearchBarTableViewController, see the link and below is mine code for FKRSearchBarTableViewController
- (id)initWithSectionIndexes:(BOOL)showSectionIndexes withDataSource:(NSArray*) dataSource withControllerId:(int) ControllerId forGroup:(int)groupId
{
if ((self = [super initWithNibName:nil bundle:nil])) {
self.title = #"Search Bar";
NSLog(#"%d",groupId);
_groupID = groupId;
_controllerId = ControllerId;
_showSectionIndexes = showSectionIndexes;
_famousPersons = [[NSMutableArray alloc]initWithArray:dataSource];
if (showSectionIndexes) {
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSMutableArray *unsortedSections = [[NSMutableArray alloc] initWithCapacity:[[collation sectionTitles] count]];
for (NSUInteger i = 0; i < [[collation sectionTitles] count]; i++) {
[unsortedSections addObject:[NSMutableArray array]];
}
if(ControllerId == 5)
{
for (Person *personName in self.famousPersons) {
// NSInteger index = [collation sectionForObject:[personName objectForKey:#"FirstName"] collationStringSelector:#selector(description)];
NSLog(#"%#",personName.firstName);
NSInteger index = [collation sectionForObject:personName.firstName collationStringSelector:#selector(description)];
[[unsortedSections objectAtIndex:index] addObject:personName];
}
}
else
{
for (NSDictionary *personName in self.famousPersons) {
NSInteger index = [collation sectionForObject:[personName objectForKey:#"FirstName"] collationStringSelector:#selector(description)];
[[unsortedSections objectAtIndex:index] addObject:personName];
}
}
NSMutableArray *sortedSections = [[NSMutableArray alloc] initWithCapacity:unsortedSections.count];
for (NSMutableArray *section in unsortedSections) {
[sortedSections addObject:[NSMutableArray arrayWithArray:[collation sortedArrayFromArray:section collationStringSelector:#selector(description)]]];
}
self.sections = [NSMutableArray arrayWithArray:sortedSections];
}
to make the list more dynamic, solution should be
// given NSArray names = your full list of name
// NSArray indexes = your list of index
NSMutableArray *nameSections = [NSMutableArray arrayWithCapacity:26];
NSMutableArray *filteredIndexes = [NSMutableArray arrayWithCapacity:26];
for (NSString *index in indexes) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"SELF beginswith[c] %#",index];
NSArray *filterNames = [names filteredArrayUsingPredicate:predicate];
if(filterNames.count>0){
[nameSections addObject:filterNames];
[filteredIndexes addObject:index];
}
}
NSLog(#"filteredIndexes %#",filteredIndexes);
NSLog(#"nameSections %#",nameSections);
numOfSection = nameSections.count
numOfRow = [[numOfSection indexOfObject:section]count];
name = [[numOfSection indexOfObject:section]] indexOfObject:row];
// print log
//given indexes array a~z
names (
"a_string",
"a_string2",
"b_string",
"b_string2"
)
filteredIndexes (
a,
b
)
nameSections (
(
"a_string",
"a_string2"
),
(
"b_string",
"b_string2"
)
)

Resources