Show distinct results in fetch request, group by an attribute and calculate the total for that attribute

本秂侑毒 提交于 2019-12-20 02:00:14

问题


Scenario:

I have an expense tracking iOS Application and I have a view controller called "DashBoardViewController" (table view controller - with FRC) which would basically categorize my expenses/incomes for a given week, a month, or year and display it as the section header title for example : (Oct 1- Oct 7, 2012) and it shows expenses/incomes ROWS and related stuff according to that particular week or month or year.

My Question:

What I want to accomplish is :

Suppose I save 3 new expenses with SAME category named "Auto" on three different dates(11 nov, 14 nov, 16 nov, 2012 respectively).

In my view controller, I want to display that category "Auto" as a row in table view but it should appear only as ONE ROW and NOT THREE TIMES as I saved three expenses (with category "Auto") and the total amount should be added up for all the 3 expenses I saved (for that particular category). Something like the following screenshot.

I have written some code bit it gives me THREE ROWS for the SAME CATEGORY and not what I actually want (ONE ROW for SAME CATEGORY) and I don't know how I would calculate the total for them? Should be something related to NSPredicate here or fetched results controller. Any help would be highly appreciated.

- (void)userDidSelectStartDate:(NSDate *)startDate andEndDate:(NSDate *)endDate
{
    AppDelegate * applicationDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext * context = [applicationDelegate managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
  // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Money" inManagedObjectContext:context];
   [fetchRequest setEntity:entity];

  // Set the batch size to a suitable number.
   [fetchRequest setFetchBatchSize:20];

   NSPredicate *predicateDate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", startDate, endDate];

// Edit the sort key as appropriate.

  typeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"type" ascending:YES]; // type refers to an expense or income.

  dateSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];

  if(self.choiceSegmentedControl.selectedIndex == 0)  // UISegment Control for "Sorting Category"
  {
      NSPredicate *predicateCategory = [NSPredicate predicateWithFormat:@"cat == %@", @""];

      NSArray * subPredicates = [NSArray arrayWithObjects:predicateCategory, predicateDate, nil];

      NSPredicate * compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];

      [fetchRequest setPredicate:compoundPredicate];

      choiceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"cat" ascending:NO];
   }

   NSArray * descriptors = [NSArray arrayWithObjects:typeSortDescriptor, dateSortDescriptor, choiceSortDescriptor, nil];

   [fetchRequest setSortDescriptors:descriptors];
   [fetchRequest setIncludesSubentities:YES];


   if(_fetchedResultsController)
   {
       [_fetchedResultsController release]; _fetchedResultsController = nil;

   }

   // Edit the section name key path and cache name if appropriate.
   // nil for section name key path means "no sections".
  _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:@"type" cacheName:nil];

  _fetchedResultsController.delegate = self;

  NSError *anyError = nil;

  if(![_fetchedResultsController performFetch:&anyError])
  {
      NSLog(@"error fetching:%@", anyError);
  }

  __block double totalAmount = 0;

  [[self.fetchedResultsController fetchedObjects] enumerateObjectsUsingBlock: ^void (Money *money, NSUInteger idx, BOOL *stop) {

    totalAmount += [[money amount] doubleValue];
  }];

  [fetchRequest release];

  //Finally you tell the tableView to reload it's data, it will then ask your NEW FRC for the new data
  [self.dashBoardTblView reloadData];

  self.startDate = startDate;
  self.endDate = endDate;

}

I thought to use NSDictionaryResultType but that's giving a problem with the FRC i have used ( for section names, filling up the table view etc.)

The code where I loop through the FRC gives me the total amount (for income and expenses) BUT I want the total amount for each category (example: total for category "Auto", total for category "Entertainment"). Please help me, I am totally stucked up here.


回答1:


I don't think you can massage your FRC into returning the kind of objects you need. NSPredicate just filters the kind of objects to return it does not create new ones from the data.

However, you can fetch the your money objects filtered by the date and then calculate the data from the array of money objects using KVC Collection Operators like so:

NSArray *moneyObjectsFilteredbyDate = [self.fetchedResultsController fetchedObjects]
NSArray *categoryStrings = [moneyObjectsFilteredbyDate valueForKeyPath:@"@distinctUnionOfObjects.cat"];
NSArray *sortedCategoryStrings = [categoryStrings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

NSMutableArray *aggregatedDataObjects = [NSMutableArray array];
for (NSString *aCategoryString in sortedCategoryStrings) {
    NSPredictate *categoryPredicate = [NSPredicate predicateWithFormat:@"cat == %@", aCategoryString];
    NSArray *moneyWithThisCategory  = [moneyObjectsFilteredByDate filteredArrayUsingPredicate:categoryPredicate];
    NSNumber *sum = [moneyWithThisCategory valueForKeyPath:@"@sum.amount"];
    [aggregatedDataObjects addObject:@{@"category" : aCategoryString, @"sum" : sum, @"individualExpenses" : moneyWithThisCategory}];
} 

Of course, you could do parts of the in the method where you configure the table cell (like calculating the sum itself), but I hope it gives you an idea. But I don't think you can use the predicate in a form of an SQL query or similar to create new data structure.

Something else you could do: Make the category an individual object of your Core Data model and add a relationship between moneyobjects and Category objects. Then you can just fetch category objects. Although you would then have to filter the expense for a category by the dates.



来源:https://stackoverflow.com/questions/13427436/show-distinct-results-in-fetch-request-group-by-an-attribute-and-calculate-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!