1

I have a horizontally and vertically scrollable table. I get the data for the header and first column from the web service(json). I want to sort the data in ascending order and remove duplicate data from both header and the first column. For removing duplicate values I used the following code:

-(void) requestFinished: (ASIHTTPRequest *) request
{
    NSString *theJSON = [request responseString];

    SBJsonParser *parser = [[SBJsonParser alloc] init];

    NSMutableArray *jsonDictionary = [parser objectWithString:theJSON error:nil];

    headData = [[NSMutableArray alloc] init];
    NSMutableArray *head = [[NSMutableArray alloc] init];

    leftTableData = [[NSMutableArray alloc] init];
    NSMutableArray *left = [[NSMutableArray alloc] init];

    rightTableData = [[NSMutableArray alloc]init];

    for (NSMutableArray *dictionary in jsonDictionary)
    {
        Model *model = [[Model alloc]init];

        model.cid = [[dictionary valueForKey:@"cid"]intValue];
        model.iid = [[dictionary valueForKey:@"iid"]intValue];
        model.yr = [[dictionary valueForKey:@"yr"]intValue];
        model.val = [dictionary valueForKey:@"val"];

        [mainTableData addObject:model];

        [head addObject:[NSString stringWithFormat:@"%ld", model.yr]];
        [left addObject:[NSString stringWithFormat:@"%ld", model.iid]];
    }
    NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:head];
    headData = [[orderedSet array] mutableCopy];

//    NSSet *set = [NSSet setWithArray:left];
//    NSArray *array2 = [set allObjects];
//    NSLog(@"%@", array2);

    NSOrderedSet *orderedSet1 = [NSOrderedSet orderedSetWithArray:left];
    NSMutableArray *arrLeft = [[orderedSet1 array] mutableCopy];

    //remove duplicate enteries from header array
    [leftTableData addObject:arrLeft];


    NSMutableArray *right = [[NSMutableArray alloc]init];
    for (int i = 0; i < arrLeft.count; i++)
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        for (int j = 0; j < headData.count; j++)
        {
            /* NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.iid == %ld", [[arrLeft objectAtIndex:i] intValue]];
             NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];*/
            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.iid == %ld AND SELF.yr == %ld", [[arrLeft objectAtIndex:i] intValue], [[headData objectAtIndex:j] intValue]];
            NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];

            if([filteredArray count]>0)
            {
                Model *model = [filteredArray objectAtIndex:0];
                [array addObject:model.val];
            }
        }
        [right addObject:array];
    }
    [rightTableData addObject:right];
}

How will I sort the arrays in ascending order?

Please help.

Itaws
  • 330
  • 1
  • 22
  • may this link will help you. [link]http://stackoverflow.com/questions/1025674/the-best-way-to-remove-duplicate-values-from-nsmutablearray-in-objective-c – Jatin Patel - JP Apr 13 '15 at 05:33
  • I have already removed duplicate values. I want to set the data in ascending order. – Itaws Apr 13 '15 at 05:48
  • kindly followed this link. http://stackoverflow.com/questions/15540829/sorting-array-using-nssortdescriptor – Jatin Patel - JP Apr 13 '15 at 06:07
  • For all of the answers you have said that you are getting the error about iid. This is your problem. This is not something wrong with the answers, this is something wrong with your code, your model. All of these answers work. You have given very little information about your data so it's not possible to see why this is happening. Can you include your model code and the sorting code that is showing his error. – Fogmeister Apr 13 '15 at 06:20
  • Also, you don't need to be using SBJSONParser. Just use NSJSONSerialization. – Fogmeister Apr 13 '15 at 06:33
  • Also, don't call an array blahDictionary. It isn't a dictionary. – Fogmeister Apr 13 '15 at 06:34
  • Ok, please can you explain what it is you are trying to do. It looks like you are trying to display information grouped by year or something. I think there will be a much better way of approaching this if I knew what you were trying to achieve. – Fogmeister Apr 13 '15 at 06:36
  • I have a horizontally and vertically scrollable table with the header section having years from 1960 to 2013. The left column has indicator id and I have values according to the year and indicator as the content. I want to sort the year and indicator id and also remove duplicate values and get the data accordingly – Itaws Apr 13 '15 at 06:41
  • You should create a custom model to store this. Using lots of arrays is a very confusing way to do this. The duplicates that you are trying to remove don't need to exist in the first place. Use a dictionary to store arrays of items using the year as the key. – Fogmeister Apr 13 '15 at 06:48
  • I will write an answer when I get to my computer. Too complicated for my phone. Lol! – Fogmeister Apr 13 '15 at 06:54
  • @Itaws I have added an answer that shows a completely different approach that should make everything much easier and much cleaner for you. – Fogmeister Apr 13 '15 at 08:19

4 Answers4

1

OK, so you have a model object that looks something like this...

@interface Model: NSObject

@property NSNumber *idNumber;
@property NSNumber *year;
@property NSString *value;

@end

Note, I am intentionally using NSNumber and not NSInteger for reasons that will become clear.

At the moment you are trying to do a lot all in one place. Don't do this.

Create a new object to store this data. You can then add methods to get the data you need. Seeing as you are displaying in a table view sectioned by year and then each section ordered by idNumber then I'd do something like this...

@interface ObjectStore: NSObject

- (void)addModelObject:(Model *)model;

// standard table information
- (NSInteger)numberOfYears;
- (NSInteger)numberOfIdsForSection:(NSinteger)section;

// convenience methods
- (NSNumber *)yearForSection:(NSInteger)section;
- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row;
- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row;

// now you need a way to add objects
- (void)addModelObject:(Model *)model;

@end

Now to implement it.

We are going to store everything in one dictionary. The keys will be years and the objects will be dictionaries. In these dictionaries the keys will be idNumbers and the objects will be arrays. These array will hold the models.

So like this...

{
    2010 : {
        1 : [a, b, c],
        3 : [c, d, e]
    },
    2013 : {
        1 : [g, h, u],
        2 : [e, j, s]
    }
}

We'll do this with all the convenience methods also.

@interface ObjectStore: NSObject

@property NSMutableDictionary *objectDictionary;

@end

@implementation ObjectStore

 + (instancetype)init
{
    self = [super init];

    if (self) {
        self.objectDictionary = [NSMutableDictionary dictionary];
    }

    return self;
}

 + (NSInteger)numberOfYears
{
    return self.objectDictionary.count;
}

 + (NSInteger)numberOfIdsForSection:(NSinteger)section
{
    // we need to get the year for this section in order of the years.
    // lets create a method to do that for us.
    NSNumber *year = [self yearForSection:section];

    NSDictionary *idsForYear = self.objectDictionary[year];

    return idsForYear.count;
}

- (NSNumber *)yearForSection:(NSInteger)section
{
    // get all the years and sort them in order
    NSArray *years = [[self.obejctDictionary allKeys] sortedArrayUsingSelector:@selector(compare:)];

    // return the correct year
    return years[section];
}

- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row
{
    // same as the year function but for id
    NSNumber *year = [self yearForSection:section];

    NSArray *idNumbers = [[self.objectDictionary allKeys]sortedArrayUsingSelector:@selector(compare:)];

    return idNumbers[row];
}

- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row
{
    NSNumber *year = [self yearForSection:section];
    NSNumber *idNumber = [self idForSection:section row:row];

    return self.objectDictionary[year][idNumber];
}

// now we need a way to add objects that will put them into the correct place.

- (void)addModelObject:(Model *)model
{
    NSNumber *modelYear = model.year;
    NSNumber *modelId = model.idNumber;

    // get the correct storage location out of the object dictionary
    NSMutableDictionary *idDictionary = [self.objectDictionary[modelYear] mutableCopy];

    // there is a better way to do this but can't think atm
    if (!idDictionary) {
        idDictionary = [NSMutableDictionary dictionary];
    }

    NSMutableArray *modelArray = [idDictionary[modelId] mutableCopy];

    if (!modelArray) {
        modelArray = [NSMutableArray array];
    }

    // insert the model in the correct place.
    [modelArray addObject:model];
    idDictionary[modelId] = modelArray;
    self.objectDictionary[modelYear] = idDictionary;
}

@end

With all this set up you can now replace your complex function with this...

-(void) requestFinished: (ASIHTTPRequest *) request
{
    NSString *theJSON = [request responseString];
    SBJsonParser *parser = [[SBJsonParser alloc] init];
    NSDictionary *jsonDictionary = [parser objectWithString:theJSON error:nil];

    for (NSDictionary *dictionary in jsonDictionary)
    {
        Model *model = [[Model alloc]init];

        model.cid = [dictionary valueForKey:@"cid"];
        model.idNumber = [dictionary valueForKey:@"iid"];
        model.year = [dictionary valueForKey:@"yr"];
        model.val = [dictionary valueForKey:@"val"];

        [self.objectStore addModelObject:model];
    }
}

To get the models out for a particular row then just use...

[self.objectStore modelsForSection:indexPath.section row:indexPath.row];

To get the number of sections in the tableview delegate method...

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [self.objectStore numberOfYears];
}

No messing around with the model in the view controller.

Welcome to the MVC pattern.

There's a crap ton of code here but by placing all the code here you can remove all the complex code from your VC.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
0

NSSet keeps only non-duplicate objects within themselves so to keep only unique objects in array you can use NSSet as -

Suppose you have array with duplicate objects

NSArray *arrayA = @[@"a", @"b", @"a", @"c", @"a"];
NSLog(@"arrayA is: %@", arrayA);

//create a set with the objects from above array as
//the set will not contain the duplicate objects from above array
NSSet *set = [NSSet setWithArray: arrayA];

// create another array from the objects of the set
NSArray *arrayB = [set allObjects];
NSLog(@"arrayB is: %@", set);

The output from the above looks like:

arrayA is: (
    a,
    b,
    a,
    c,
    a
)
arrayB is: {(
    b,
    c,
    a
)}

and to sort a mutable array in ascending order you can use NSSortDescriptor and sortUsingDescriptors:sortDescriptors. Also you need to provide the key on the basis of which array will be sorted.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"key" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[array sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
Sanjay Mohnani
  • 5,947
  • 30
  • 46
  • I tried it. I gives error Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x7f8dad8a03d0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key iid.' – Itaws Apr 13 '15 at 05:48
  • can you provide the error message, also have you specified the key in sortDEscriptor? – Sanjay Mohnani Apr 13 '15 at 05:49
  • I have defined model as interface Model : NSObject property (assign, nonatomic) NSInteger cid; property (assign, nonatomic) NSInteger iid; property (assign, nonatomic) NSInteger yr; property (strong, nonatomic) NSString *val; – Itaws Apr 13 '15 at 06:00
  • I have to sort iid and yr – Itaws Apr 13 '15 at 06:05
  • NSSortDescriptor *iidDescriptor = [[NSSortDescriptor alloc] initWithKey:@"iid" ascending:YES]; NSSortDescriptor *yrDescriptor = [[NSSortDescriptor alloc] initWithKey:@"yr" ascending:YES]; NSArray *sortDescriptors = @[iidDescriptor, yrDescriptor]; NSArray *sortedArray = [array sortedArrayUsingDescriptors:sortDescriptors]; – Sanjay Mohnani Apr 13 '15 at 06:18
0

Here you will get what you want.

//sort description will used to sort array.
NSSortDescriptor *descriptor=[[NSSortDescriptor alloc] initWithKey:@"iid" ascending:YES];
NSArray *descriptors=[NSArray arrayWithObject: descriptor];
NSArray *reverseOrder=[arrLeft sortedArrayUsingDescriptors:descriptors];

reverseOrder is your desire output.

there is another way you can sort objects that followed model.

NSArray *someArray = /* however you get an array */    
NSArray *sortedArray = [someArray sortedArrayUsingComparator:^(id obj1, id obj2) {
 NSNumber *rank1 = [obj1 valueForKeyPath:@"iid"];
NSNumber *rank2 = [obj2 valueForKeyPath:@"iid"];
return (NSComparisonResult)[rank1 compare:rank2];
}];

here sortedArray is our output.

you can replace same things for yr key as well.

Jatin Patel - JP
  • 3,725
  • 2
  • 21
  • 43
  • Getting error this class is not key value coding-compliant for the key iid.' – Itaws Apr 13 '15 at 06:13
  • @Itaws, are you getting compiler error or crash ? actually this code is working over here so i think there is some minor changes left at your side. can you please give me your `arrLeft` ? – Jatin Patel - JP Apr 13 '15 at 06:21
  • may be your error due to following reason.http://stackoverflow.com/questions/3088059/this-class-is-not-key-value-coding-compliant-for-the-key – Jatin Patel - JP Apr 13 '15 at 06:28
0

This is what I did to sort the header data in ascending order and to remove duplicates from both header and leftmost column. Hope this will help others

NSOrderedSet *orderedSet3 = [NSOrderedSet orderedSetWithArray:head3];
headData3 = [[orderedSet3 array] mutableCopy];
[headData3 sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2)
{
    return [str1 compare:str2 options:(NSNumericSearch)];
}];

NSOrderedSet *orderedSet4 = [NSOrderedSet orderedSetWithArray:left3];
NSMutableArray *arrLeft3 = [[orderedSet4 array] mutableCopy];

[leftTableData3 addObject:arrLeft3];
Itaws
  • 330
  • 1
  • 22