0

I have an array(say expensesArray) of Expense objects. expense has property like catId, amount, name etc. Now I want to do something like group by and some other clauses in SQL. i.e in new array, my objects should be grouped based on catId and their amount should be added up. For example, if I have 2 elements in expensesArray with catId=2 and amount=20 for each then the new array should contain only one element with catId=2 and amount=40. This is the code that I have tried yet :

[groupedExpesesArray addObject:[self createExpenseModel:[expensesArray objectAtIndex:0]]];

ExpenseModel* expenseMObj = [groupedExpesesArray objectAtIndex:0];

for (int j = 1; j < expensesArray.count; j++) 
{            
    ExpenseModel* expModel = [self createExpenseModel:[expensesArray objectAtIndex:j]];

    if ([expModel.categoryID isEqual:expenseMObj.categoryID])
    {
      float amt = [expenseMObj.amount floatValue];
      amt = amt+[expModel.amount doubleValue];
      expenseMObj.amount = [NSNumber numberWithFloat:amt];
    }
    else
    {
      int index = [groupedExpesesArray indexOfObject:expModel];
      if (index == NSNotFound)
      {
         [groupedExpesesArray addObject:expModel];
      }
    }
 }

But I am still unable to get the desired value. So I need some guidance here on where I am going wrong here and possible resolving steps.

Edit:- I am creating new object of Expense in createExpenseModel: method. and checking if it is available in groupedExpesesArray. perhaps thats the problem. Isn't it? Any suggestion would be appreciated. Thanks.

3 Answers 3

0

Based on this answer:

NSArray *array = @[@{@"groupId" : @"1", @"price" : @20},
                   @{@"groupId" : @"2", @"price" : @1},
                   @{@"groupId" : @"3", @"price" : @2},
                   @{@"groupId" : @"4", @"price" : @3},
                   @{@"groupId" : @"1", @"price" : @30},
                   @{@"groupId" : @"2", @"price" : @5},
                   @{@"groupId" : @"3", @"price" : @100},
                   @{@"groupId" : @"4", @"price" : @7},
                   @{@"groupId" : @"1", @"price" : @200},
                   @{@"groupId" : @"2", @"price" : @9},
                   @{@"groupId" : @"3", @"price" : @10},
                   ];

NSMutableArray *resultArray = [NSMutableArray new];
NSArray *groups = [array valueForKeyPath:@"@distinctUnionOfObjects.groupId"];
for (NSString *groupId in groups)
{
    NSMutableDictionary *entry = [NSMutableDictionary new];
    [entry setObject:groupId forKey:@"groupId"];

    NSArray *groupedEntires = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"groupId = %@", groupId]];
    CGFloat sum = 0;
    for (NSDictionary *dict in groupedEntires)
    {
        NSNumber *price = [dict objectForKey:@"price"];
        sum += [price floatValue];

    }
    [entry setObject:@(sum) forKey:@"price"];
    sum = 0;
    [resultArray addObject:entry];
}

Result:

{
    groupId = 3;
    price = 112;
},
{
    groupId = 4;
    price = 10;
},
{
    groupId = 1;
    price = 250;
},
{
    groupId = 2;
    price = 15;
}
)
0

I don't know if I understood well but, it could be done by something like this

//This will filter the array grouping object by the same ID
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"catID == %d",index];
NSArray * arrayGroup = [expensesArray filteredArrayUsingPredicate:predicate];
//Sum their amount
NSNumber * totalAmount = [arrayGroup valueForKeyPath:@"@sum.amount"];

You can put that into a loop and recreate the values by taking the first object of each single filtered group array. Probably it's not the fastest way but it should work.

0

Assuming you have a model object class like this:

Expense.h

NS_ENUM(NSUInteger, ExpenseType) {
    ExpenseTypeMeals,
    ExpenseTypeLodging,
    ExpenseTypeTravel
};

@interface Expense : NSObject

@property (assign, nonatomic) NSUInteger categoryID;
@property (assign, nonatomic) float amount;

- (instancetype)initWithCategoryID:(NSUInteger)categoryID amount:(float)amount;
+ (instancetype)expenseWithCategoryID:(NSUInteger)categoryID amount:(float)amount;
@end

Expense.m

@implementation Expense

- (instancetype)initWithCategoryID:(NSUInteger)categoryID amount:(float)amount
{
    if (!(self = [super init])) return nil;
    _categoryID = categoryID;
    _amount = amount;
    return self;
}

+ (instancetype)expenseWithCategoryID:(NSUInteger)categoryID amount:(float)amount
{
    return [[Expense alloc] initWithCategoryID:categoryID amount:amount];
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"categoryID: %@, amount: %.2f", @(self.categoryID), self.amount];
}
@end

... and given an array of Expense objects like so:

NSArray *expenses = @[[Expense expenseWithCategoryID:ExpenseTypeMeals amount:32.50],
                      [Expense expenseWithCategoryID:ExpenseTypeMeals amount:12.95],
                      [Expense expenseWithCategoryID:ExpenseTypeLodging amount:125.45],
                      [Expense expenseWithCategoryID:ExpenseTypeLodging amount:125.45]];

... you could then do something similar to the following:

// Use Key-Value Coding (KVC) collection operator to obtain an array of category IDs.
NSArray *categoryIDs = [expenses valueForKeyPath:@"@distinctUnionOfObjects.categoryID"];
NSMutableArray *totalsByCategory = [NSMutableArray arrayWithCapacity:categoryIDs.count];

for (NSNumber *categoryID in categoryIDs)
{
    // Use a predicate to a filtered array of Expense objects that match the current category ID.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"categoryID = %@", categoryID];
    NSArray *expensesForCategory = [expenses filteredArrayUsingPredicate:predicate];

    // Use a KVC collection operator to sum of the amount properties of the Expense objects.
    NSNumber *sum = [expensesForCategory valueForKeyPath:@"@sum.amount"];

    [totalsByCategory addObject:[Expense expenseWithCategoryID:categoryID.intValue amount:sum.floatValue]];
}

NSLog(@"%@", totalsByCategory);

... with the the resulting output...

2014-08-12 11:08:32.692 MyExpenses[9691:303] (
    "categoryID: 0, amount: 45.45",
    "categoryID: 1, amount: 250.90"
)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.