NSMutableArray sorting and displaying in UITableView - uitableview

I need to show a grouped tableview from the below data. I need to categorise the below array based on "account_type".
For Eg: I need to show Table Section Heading "Savings" and list all savings type accounts, then similarly get Unique account types and gave that as section header and account numbers in table rows. I am able to get section headers using NSSet, but how to get row counts and display it in a UITableView.
<__NSArrayM 0x7f8ef1e8b790>(
{
"account_no" = 123;
"account_type" = Savings;
},
{
"account_no" = 123456;
"account_type" = Savings;
},
{
"account_no" = 00000316;
"account_type" = "DPN STAFF NON EMI";
},
{
"account_no" = 1000000552;
"account_type" = "DPN STAFF EMI LOANS";
})
I need to display the above data in UITableView like
section 0 --- Savings
Row 1 - 123
Row 2 - 123456
section 1 ---> DPN STAFF NON EMI
Row 1 - 00000316
Thanks,
AKC

You can make use of NSDictionary also. The below code worked perfectly.
if([arrySelectedDetails count] >0){
grouped = [[NSMutableDictionary alloc] initWithCapacity:arrySelectedAcctDetails.count];
for (NSDictionary *dict in arrySelectedDetails) {
id key = [dict valueForKey:#"type"];
NSMutableArray *tmp = [grouped objectForKey:key];
if (tmp == nil) {
tmp = [[NSMutableArray alloc] init];
[grouped setObject:tmp forKey:key];
}
[tmp addObject:dict];
}
typeArray= [[NSMutableArray alloc]init];
for(NSDictionary *groupId in arrySelectedDetails){
if(!([typeArray count]>0)){
[typeArray addObject:[groupId valueForKey:#"type"]];
}
else if (![typeArray containsObject:[groupId valueForKey:#"type"]]) {
[typeArray addObject:[groupId valueForKey:#"type"]];
}
}
}
Then for UITableView Delegates:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [typeArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [grouped[[typeArray objectAtIndex:section]] count]
}

Try the below code:
NSMutableArray *resultArray = [NSMutableArray new];
NSArray *groups = [arrySelectedAcctDetails valueForKeyPath:#"#distinctUnionOfObjects.account_type"];
NSLog(#"%#", groups);
for (NSString *groupId in groups)
{
NSMutableDictionary *entry = [NSMutableDictionary new];
[entry setObject:groupId forKey:#"account_type"];
NSArray *groupNames = [arrySelectedAcctDetails filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"account_type = %#", groupId]];
for (int i = 0; i < groupNames.count; i++)
{
NSString *name = [[groupNames objectAtIndex:i] objectForKey:#"account_no"];
[entry setObject:name forKey:[NSString stringWithFormat:#"account_no%d", i + 1]];
}
[resultArray addObject:entry];
}
NSLog(#"%#", resultArray);
Output:
{
"account_no1" = 00000316;
"account_type" = "DPN STAFF NON EMI";
},
{
"account_no1" = 123;
"account_no2" = 123456;
"account_type" = Savings;
},

Related

Does CHCSVParser have a column parsing limit?

In my application you can import data through a tab separated values file. I don't have any challenge until I parse "locations" that have multiple items attached to them. If you scroll to the very bottom of the second method you can see how I create a relationship between items and the locations that contain them inside Core Data. The problem occurs when I parse past column 31 in a location. It doesn't attach those items to the location. So my question is this; is there a limit to columns in the NSArray that is parsed by CHCSVParser? If not, what would cause this limiting to 31 columns?
I've posted the two methods that I encounter the bug with below.
+ (void) importDatabaseTSVURL:(NSURL*)url {
// First check if there is already a database. If so, stop import.
if ([XSELLocation locations].count > 0) return;
if ([XSELItem items].count > 0) return;
if ([XSELVendor vendors].count > 0) return;
NSError *error;
NSArray *array = [NSArray arrayWithContentsOfDelimitedURL:url options:CHCSVParserOptionsSanitizesFields delimiter:'\t' error:&error];
if ([[[array firstObject] firstObject] isEqualToString:#"XSELINVENTORYTSV"]) {
for (NSArray *row in array) {
[XSELSettings parseImportDataRow:row];
}
}
}
+ (void) parseImportDataRow:(NSArray*)array {
// Create logic to seperate data entered next
static NSString *operation = #"none";
if ([array.firstObject isEqualToString:#"ITEMLIST"]) {
operation = #"items";
return;
}
else if ([array.firstObject isEqualToString:#"LOCATIONLIST"]) {
operation = #"locations";
return;
}
else if ([array.firstObject isEqualToString:#"VENDORLIST"]) {
operation = #"vendors";
return;
}
else if ([array.firstObject isEqualToString:#"ENDLIST"]) { // Create database, relate objects, and clean up the data
operation = #"none";
return;
}
// Parse rows to the correct array.
if ([operation isEqualToString:#"vendors"]) {
NSLog(#"adding vendor");
XSELVendor *vendor = [XSELVendor addVendor];
vendor.vendorID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextVendorID];
vendor.name = [array objectAtIndex:1];
vendor.contactID = [array objectAtIndex:2];
}
else if ([operation isEqualToString:#"items"]) {
NSLog(#"adding item");
XSELItem *item = [XSELItem addItem];
item.itemID = [NSNumber numberWithDouble:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextItemID];
item.name = [array objectAtIndex:1];
item.smallPackageName = [array objectAtIndex:2];
item.bigPackageName = [array objectAtIndex:3];
item.smallPerBig = [NSNumber numberWithDouble:[[array objectAtIndex:4] integerValue]];
item.buildTo = [NSNumber numberWithDouble:[[array objectAtIndex:5] integerValue]];
item.price = [NSNumber numberWithDouble:[[array objectAtIndex:6] integerValue]];
// Relate preferred vendor to item
for (XSELVendor *vendor in [XSELVendor vendors]) {
if ([vendor.vendorID.stringValue isEqualToString:[array objectAtIndex:7]]) {
item.preferredVendor = vendor;
break;
}
}
}
else if ([operation isEqualToString:#"locations"]) {
NSLog(#"adding location");
XSELLocation *location = [XSELLocation addLocation:[array objectAtIndex:1]];
location.locationID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextLocationID];
location.position = [NSNumber numberWithInteger:[[array objectAtIndex:2] integerValue]];
// Relate location with items
unsigned long itemsRelatedCount = array.count - 3;
NSLog(#"\n\nitemsRelated: %lu\n\n", itemsRelatedCount);
NSMutableOrderedSet *items = [NSMutableOrderedSet orderedSet];
for (int i = 0; i < itemsRelatedCount; i++) {
NSString *itemID = [array objectAtIndex:i];
for (XSELItem *item in [XSELItem items]) {
if ([item.itemID.stringValue isEqualToString:itemID]) {
[items addObject:item];
break;
}
}
}
location.items = items;
}
}

Sort NSArray custom objects by another NSArray custom objects

I have 2 different NSArray with custom objects as follows,
Item *item1 = [[Items alloc] init];
item1.number = #"1";
item1.serailNumber = #"S01";
Item *item2 = [[Items alloc] init];
item2.number = #"2";
item2.serailNumber = #"S02";
Item *item3 = [[Items alloc] init];
item3.number = #"3";
item3.serailNumber = #"S03";
Item *item4 = [[Items alloc] init];
item4.number = #"4";
item4.serailNumber = #"S04";
Item *item5 = [[Items alloc] init];
item5.number = #"5";
item5.serailNumber = #"S05";
NSArray *items = #[item1, item2, item3, item4, item5];
NSArray *specList = #[#{"number" : #"002", #"serialNumber" : #"S02"},
#{"number" : #"004", #"serialNumber" : #"S04"},
#{"number" : #"003", #"serialNumber" : #"S03"}];
Now I want to sort my items array based on specList array by comparing "number" property.
Now my expected items list is,
#[item2, item4, item3, item1, item5]
I have gone through several samples as listed below but I couldn't figure out how to compare with custom objects. Any help would be appreciated, Thanks in advance.
Sample 1
Sample 2
This should do the trick:
NSArray *sorted = [items sortedArrayUsingComparator:^NSComparisonResult(Item *item1, Item *item2) {
NSInteger indexForItemEquivalent1InSpecList = [self indexForItem:item1 inList:specList];
NSInteger indexForItemEquivalent2InSpecList = [self indexForItem:item2 inList:specList];
return [#(indexForItemEquivalent1InSpecList) compare:#(indexForItemEquivalent2InSpecList)];
}];
NSLog(#"Sorted: %#", sorted);
With:
-(NSInteger)indexForItem:(Item *)item inList:(NSArray *)list
{
for (NSInteger i = 0; i < [list count]; i++)
{
if ([list[i][#"number"] integerValue] == [[item number] integerValue])
{
return i;
}
}
return NSIntegerMax; //If not found, we put it at the end of the list
}
Output:
Sorted: (
"<Item 0x146678f0> number: 2 serial: S02",
"<Item 0x14667e10> number: 4 serial: S04",
"<Item 0x14667900> number: 3 serial: S03",
"<Item 0x14654200> number: 1 serial: S01",
"<Item 0x14667e20> number: 5 serial: S05"
)
I override -description to make log clearer:
-(NSString *)description
{
return [NSString stringWithFormat:#"<%# %p> number: %# serial: %#", [self class], self, _number, _serailNumber];
}
In other words:
You have to find the index of the corresponding Item object inside specList (see indexForItem:inList:). I used integerValue because you are using #"002" and #"2", which aren't equal strings.
Then in the NSComparator you compare the two indexes.
For item1 and item5 which are at the end, I let them as if. There is no guarantee of their order since they are not present in specList. If you want to put them in "ascending" order, you have to do instead:
NSInteger indexForItem1InSpecList = [self indexForItem:item1 inList:specList];
NSInteger indexForItem2InSpecList = [self indexForItem:item2 inList:specList];
if (indexForItem1InSpecList == NSIntegerMax && indexForItem2InSpecList == NSIntegerMax)
{
return [#([[item1 number] integerValue]) compare:#([[item2 number] integerValue])];
}
else
{
return [#(indexForItem1InSpecList) compare:#(indexForItem2InSpecList)];
}
Here is an example of sorting second array referring to the first one:
NSArray *users = #[#"Dave",#"Mike",#"Joe",#"Jason",#"Kevin"];
NSArray *iqs = #[#110,#145,#75,#122,#130];
NSMutableArray *array = [NSMutableArray array];
for (int idx = 0;idx<[users count];idx++) {
NSDictionary *dict = #{#"Name": users[idx],#"IQ":iqs[idx]};
[array addObject:dict];
}
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"IQ" ascending:NO];
[array sortUsingDescriptors:#[descriptor]];

Setting Timeout as Activity indicator keeps spinning when there is no response from webservice

I am using REST API to fetch data. I have two methods, one for ResponceSucces and other for responceFailure. In responce failure I simply dismiss the activity indicator. Sometimes it happens that data is not retrieved but still activity indicator keeps spinning infinitely. And ResponceFailure method is not Called.
My question is can I set timeout interval to my web service, to check if response did not come, then activity indicator must be dismissed after some time for example 20/30 seconds.
Below is my code:
-(void)messagesGetSuccess:(FBListingsWebHandler*)handler response:(NSDictionary*)response
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[FBModelManager sharedModelManager].messages removeAllObjects];
NSArray *messagesArray = [response objectForKey:#"message"];
if([[FBUserManager sharedUserManager] userType] == kUserShipper){
//extract usernames of transporters who placed bid on selected listing
NSArray *userNames;
NSMutableArray *transporterNames;
NSDictionary *biddingsDic = [[FBModelManager sharedModelManager] getModelDictionary:kModelBiddings];
if ([biddingsDic count] > 0){
userNames = [[biddingsDic allValues] valueForKey:#"userName"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:userNames];
NSSet *uniqueNames = [orderedSet set];
transporterNames = [[NSMutableArray alloc] initWithArray:[uniqueNames allObjects]];
}
//add transporter records in messages dictionary who have messages for selected listing
for (int i = 1; i < [messagesArray count]; i++) {
NSMutableArray *modelArray = [[NSMutableArray alloc] init];
NSDictionary *groupedMsgDic = [messagesArray objectAtIndex:i];
int count = (int)[[[groupedMsgDic allValues] objectAtIndex:0] count];
for (int j = 0; j < count; j++) {
FBMessagesModel *model = [[FBMessagesModel alloc] initWithMessageItems:[[[groupedMsgDic allValues] objectAtIndex:0] objectAtIndex:j]];
[modelArray addObject:model];
model = nil;
}
[[FBModelManager sharedModelManager].messages setObject:modelArray forKey:[[groupedMsgDic allKeys] objectAtIndex:0]];
//remove transporter usernames from array who have messages besides bid for selected listing
if([transporterNames containsObject:[[groupedMsgDic allKeys] objectAtIndex:0]]){
[transporterNames removeObject:[[groupedMsgDic allKeys] objectAtIndex:0]];
}
modelArray = nil;
}
//add transporter records in messages dictionary who placed bid but have no message for selected listing
for (int i = 0; i < [transporterNames count]; i++) {
NSString *path = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] photoPath];
int transporterId = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] userId];
NSString *objectForZeroMsgs = [NSString stringWithFormat:#"%#-%d",path,transporterId];
[[FBModelManager sharedModelManager].messages setObject:objectForZeroMsgs forKey:[transporterNames objectAtIndex:i]];
}
transporterNames = nil;
// [self tableView:self.shipperGroupedMessagesTableView_ numberOfRowsInSection:0];
[self.shipperGroupedMessagesTableView_ reloadData];
}else if([[FBUserManager sharedUserManager] userType] == kUserTransporter){
for (int i = 1; i < [messagesArray count]; i++) {
FBMessagesModel *model = [[FBMessagesModel alloc] initWithMessageItems:[messagesArray objectAtIndex:i]];
[[FBModelManager sharedModelManager].messages setObject:model forKey:[NSString stringWithFormat:#"%d",model.mailId]];
if(model.mailMessage && ![model.mailMessage isEqualToString:#""]){
[self createMessageBubble:model];
}
model = nil;
}
[self.bubbleTableView_ reloadData];
}
}
-(void)messagesGetFailure:(FBListingsWebHandler*)handler
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[FBModelManager sharedModelManager].messages removeAllObjects];
if([[FBUserManager sharedUserManager] userType] == kUserTransporter){
[self.bubbleTableView_ reloadData];
}
else if([[FBUserManager sharedUserManager] userType] == kUserShipper){
if([[FBModelManager sharedModelManager].messages count] == 0){
NSDictionary *biddingsDic = [[FBModelManager sharedModelManager] getModelDictionary:kModelBiddings];
if ([biddingsDic count] > 0){
NSArray *userNames = [[biddingsDic allValues] valueForKey:#"userName"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:userNames];
NSSet *uniqueNames = [orderedSet set];
NSArray *transporterNames = [uniqueNames allObjects];
[[FBModelManager sharedModelManager].messages removeAllObjects];
for (int i = 0; i < [transporterNames count]; i++) {
NSString *path = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] photoPath];
int transporterId = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] userId];
NSString *objectForZeroMsgs = [NSString stringWithFormat:#"%#-%d",path,transporterId];
[[FBModelManager sharedModelManager].messages setObject:objectForZeroMsgs forKey:[transporterNames objectAtIndex:i]];
}
[self.shipperGroupedMessagesTableView_ reloadData];
}else{
[self addNoMessagesLabel];
}
}
[self.shipperGroupedMessagesTableView_ reloadData];
}
}
I am adding a screenshot.

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