Imagine I have the following NSDictionary
dict = {
"a" = [
obj1,
obj2,
obj3
],
"b" = [
obj4,
obj5
],
"invalid" = [
obj6,
obj7,
obj8
],
"c" = [
obj9,
obj10,
obj11
]
}
This data is used to populate a TableView using sections from the following NSArray:
arr = #[#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I", #"J", #"K", #"L", #"M", #"N", #"O", #"P", #"Q", #"R", #"S", #"T", #"U", #"V", #"W", #"X", #"Y", #"Z"];
I have the following method which I use to find an object
- (void)selectRowWithId:(NSNumber *)uid {
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(id == %#)", uid];
NSArray *filteredDictArray = [obj filteredArrayUsingPredicate:predicate];
if ([filteredDictArray count] > 0) {
NSIndexPath *targetIndexPath = nil;
//trying to find the NSIndexPath here
[[self tableView] selectRowAtIndexPath:targetIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
[self tableView:[self tableView] didSelectRowAtIndexPath:targetIndexPath];
}
}];
}
Say I have an object set to obj10.
Based on my NSDictionary and NSArray the NSIndexPath is Section:2 Row:1
How can I get this value if I only know obj10?
Update (Solution)
So With a combination of the ideas behind everyone's answers and my own here is the following I used in case it's helpful for someone. BTW: This is for iOS9
- (void)selectRowWithId:(NSNumber *)uid {
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(id == %#)", uid];
NSArray *filteredDictArray = [obj filteredArrayUsingPredicate:predicate];
if ([filteredDictArray count] > 0) {
//trying to find the NSIndexPath here
NSIndexPath *targetIndexPath = [NSIndexPath indexPathForRow:[[dict objectForKey:key] indexOfObject:filteredPersonsArray[0]] inSection:[arr indexOfObject:key]];
[[self tableView] selectRowAtIndexPath:targetIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
[self tableView:[self tableView] didSelectRowAtIndexPath:targetIndexPath];
}
}];
}
Your code is a little bit too tricky. Sometimes usual looping is easier.
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop)
{
for( NSInteger index = 0; index < [obj count]; index++ )
{
if ([obj[index] isEqualToString:uid])
{
// thats the index
// Let's have a look-up for the section
NSString *sectionKey = [[key substringToIndex:1] uppercaseString]; //Typo
for( NSInteger section=0; section < [arr count]; section++ )
{
if( [arr[section] isEqualToString:sectionKey] )
{
// That's the section
}
}
}
}
}
Typed in Safari.
Related
hello i am new to IOS dev, and i need to create the contacts like view, i was able to create it if i already have the arrays of contacts and indexes, but what i need is the get the contacts from the server and sort them with indexes with objective C, any help?
i checked this tutorial:
animals = #{#"B" : #[#"Bear", #"Black Swan", #"Buffalo"],
#"C" : #[#"Camel", #"Cockatoo"],
#"D" : #[#"Dog", #"Donkey"],
#"E" : #[#"Emu"],
#"G" : #[#"Giraffe", #"Greater Rhea"],
#"H" : #[#"Hippopotamus", #"Horse"],...
sectionTitles = [[animals allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
animalIndexTitles = #[#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I", #"J", #"K", #"L", #"M", #"N", #"O", #"P", #"Q", #"R", #"S", #"T", #"U", #"V", #"W", #"X", #"Y", #"Z"];
but obviously this isn't what i need,
First you have to sort all keys :
NSArray *keys = [animals allKeys];
NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [someDictionary objectForKey:a];
NSString *second = [someDictionary objectForKey:b];
return [first compare:second];
}];
after sorting keys you have to sort each array against keys .
for( i= 0 ; i < [sortedKeys count ]; i++){
NSArray * arr = [animals objectForKey:[sortedKeys objectAtIndex: i] ];
NSArray *sortedArray = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [[obj1 valueForKey:#"value"] compare:[obj2 valueForKey:#"value"]];
}];
I have two array let's say
NSArray *array1=#[ #{#"key1":#"A",#"key2":#"AA"},#{#"key1":#"C",#"key2":#"CC"},#{#"key1":#"E",#"key2":#"EE"},#{#"key1":#"G",#"key2":#"GG"}];
NSArray *array2=#[ #{#"key1":#"A",#"key2":#"AAA"},#{#"key1":#"Z",#"key2":#"ZZZ"}];
I want to subtract the array, This should be the expected reuslt,
NSArray *resultArray=#[ #{#"key1":#"C",#"key2":#"CC"},#{#"key1":#"E",#"key2":#"EE"},#{#"key1":#"G",#"key2":#"GG"}];
I tried the below code but didn't working
NSArray *extracted = [array1 valueForKey:#"key1"];
NSMutableSet *pressieContactsSet = [NSMutableSet setWithArray:extracted];
NSMutableSet *allContactSet = [NSMutableSet setWithArray:array2];
[allContactSet minusSet:pressieContactsSet];
NSLog(#"%#",allContactSet);
Please try below code
First get all key1 objects in temporary array. Then apply filter on array1 and check if your array1 object contain arrayKey1 object.
Make sure it will only check for key1 key.
NSArray *arrKey1 = [array2 valueForKey:#"key1"];
NSPredicate *pred = [NSPredicate predicateWithBlock:
^BOOL(id evaluatedObject, NSDictionary *bindings)
{
if ([arrKey1 containsObject:evaluatedObject[#"key1"]])
{
NSLog(#"found : %#",evaluatedObject);
return NO;
}
else
{
NSLog(#"Not found : %#",evaluatedObject);
return YES;
}
}];
NSArray *arrSubtracted = [array1 filteredArrayUsingPredicate:pred];
NSLog(#"%#", arrSubtracted);
Or you can use enumerateObjectsUsingBlock
NSMutableArray *resultArray = [NSMutableArray new];
[array1 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
if (![arrKey1 containsObject:obj[#"key1"]]) {
[resultArray addObject:obj];
}
}];
NSLog(#"%#",resultArray);
Hope this will help you.
This should work.
NSArray *array1=#[ #{#"key1":#"A"},#{#"key1":#"C"},#{#"key1":#"E"},#{#"key1":#"G"}];
NSArray *array2=#[ #{#"key1":#"A"},#{#"key1":#"Z"}];
NSMutableSet *pressieContactsSet = [NSMutableSet setWithArray:array1];
NSSet *allContactSet = [NSSet setWithArray:array2];
[pressieContactsSet minusSet:allContactSet];
NSArray *result = [pressieContactsSet allObjects];
NSLog(#"%#",result);
Enjoy!
NSArray *array1=#[ #{#"key1":#"A"},#{#"key1":#"C"},#{#"key1":#"E"},#{#"key1":#"G"}];
NSArray *array2=#[ #{#"key1":#"A"},#{#"key1":#"Z"}];
NSMutableArray *resultArray = [NSMutableArray arrayWithArray:array1];
[resultArray removeObjectsInArray:array2];
NSLog(#"array %#",resultArray);
I am trying to get common values from two array but insensitive match.But cannot succeed.Whats wrong with the predicate.
NSMutableArray *tempArray1 = [NSMutableArray arrayWithObjects:#"apple", #"cat", nil];
NSMutableArray *tempArray2 = [NSMutableArray arrayWithObjects:#"APPLE", #"CAT", nil];
// NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"SELF IN [cd] %#", tempArray1];
NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"SELF [cd] IN %#", tempArray1];
NSMutableArray *arr_filteredtest = [NSMutableArray arrayWithArray:[tempArray2 filteredArrayUsingPredicate:resultPredicate1]];
NSLog(#"%lu",(unsigned long)arr_filteredtest.count);
If you want case insensitive unique set from your two arrays, first you have to either lowercase or uppercase all the strings, and then create NSSet :
NSMutableArray *joindArr = [NSArray arrayWithArray:tempArray1, nil];
[joindArr addObjectsFromArray:tempArray2];
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
You can achieve intersection using NSSet, unfortunately the search using NSSet is case sensitive.
1.So convert both the arrays to common case, either upperCase or lowerCase
2.Use intersectSet to get result
Example:
NSMutableArray *tempArray1 = [NSMutableArray arrayWithObjects:#"apple", #"cat", nil];
NSMutableArray *tempArray2 = [NSMutableArray arrayWithObjects:#"APPLE", #"CAT", nil];
tempArray2 = [self convertStringObjectsToLoweCase:tempArray2]; //convert all elements to lower case/ upper case
NSSet *set1 = [NSSet setWithArray:tempArray1];
NSSet *set2 = [NSSet setWithArray:tempArray2];
NSMutableSet *mutableSet = [NSMutableSet setWithSet:set1]; //Initialize with parent array,usually array with more elements
[mutableSet intersectSet:set2];
NSArray *arrayContainingCommonObjects = [mutableSet allObjects];
NSLog(#"arrayContainingCommonObjects : %#",arrayContainingCommonObjects);
Use the following helper method
- (NSMutableArray *)convertStringObjectsToLoweCase:(NSMutableArray *)inputArray
{
[inputArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSString class]]) {
NSString *loweCaseString = [(NSString *)obj lowercaseString];
[inputArray replaceObjectAtIndex:idx withObject:loweCaseString];
}
}];
return inputArray;
}
Output:
arrayContainingCommonObjects : (
apple,
cat )
I think you should to try this one
NSMutableArray *tempArray1 = [NSMutableArray arrayWithObjects:#"apple", #"cat", nil];
NSMutableArray *tempArray2 = [NSMutableArray arrayWithObjects:#"apple", #"CAT", nil];
NSMutableSet *intersection = [NSMutableSet setWithArray:tempArray1];
[intersection intersectSet:[NSSet setWithArray:tempArray2]];
NSArray *NEW_ARR = [intersection allObjects];
NSLog(#"new arry:: %#",NEW_ARR);
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"
)
)
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);
}]];