0

The code below does what I need except it isn't ordered afterwards.(Please don't tell me its because of a NSSet is unordered I know that.) This isn't a repeated SO question either I've spent over 40 minutes reading all the "similar" questions and they don't fix the problem.

I'm trying to get an "hourly", "daily", "monthly" view of the number of times an "event" happens in a given period of time marking that time or date on the x axis of a graph, and marking the number of times it happened on the y axis. It works fine by simply resorting the dictArray below by ascending order depending on how I format the string to be displayed. Thats the problem though. For the hourly view I'm formatting the strings inside the "datesForEvent" array like this [7am, 7am, 7am, 8am, 8am, 11am, 11am, 1pm, 1pm, 3pm, 3pm] so when I sort this back to out of the NSCountedSet it will display it like this across the x axis of my graph 1pm, 3pm, 7am, 8am, 11am. Clearly not good. If I place my formatted strings into the "datesForEvent" array as a daily view like this [sun, sun, sun, mon, mon, tue, wed, wed, wed, wed, thu, thu, fri, fri fri, fri] it will be sorted like this on the x axis fri, mon, sat, sun, thu, tue, wed. The same thing happens for month view "Jul" will come before "Jun".

This makes complete sense but I'm not sure how to fix this. The problem is absolutely caused by how I format the string before it gets counted. However formatting the string after the count throws off the number of times something happens in a given period of time due the fact that the date format is the same length for all the events so it will take all these events as single occurrences and not as a grouped occurrences because they are so finely timed down to the milliseconds. In other words no event will have a duplicate time which isn't good either. I'm getting a butt kicking in date formats, date components, date styles, etc. in the Apple docs with no avail. I'm currently reading the Predicates guide while hoping for any type of help. It would be greatly appreciate for a push in the right direction.

Below is the problematic code. The ironic part is that the code is bug free because it is doing what I'm asking it to do. I need the functionality of what NSCountedSet does without the unordered part.

NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:self.datesForEvent];

NSMutableArray *groupedArray = [NSMutableArray array];
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {

    [groupedArray addObject:@{@"dates": obj,
                           @"count": @([countedSet countForObject:obj])}];

}];

//This sorting will sort alphabetically so its not good.   
NSArray *orderedArray = [[NSArray alloc]initWithArray:[groupedArray sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"dates" ascending:YES]]]];

Here is an update to the comment sort in order comment from @CRD. I took a break real quick from other work and came up with this solution to count the dates in the proper order unlike the NSOrderedSet solution that doesn't keep order. However I would love your opinion on its robustness or elegances for lack of a better word. I feel like sticking [NSNull null] at the end of the array is a bit hacky?

 #import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) NSArray *baseArray;
@property (nonatomic, strong) NSMutableArray *datesArray;
@property (nonatomic, strong) NSMutableArray *countArray;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //Base Array of counts should be - 07/1 (4), 7/2 (3), 7/3 (2), 7/4 (1)
    self.baseArray = @[@"07/01/16",@"07/01/16",@"07/01/16",@"07/01/16",
                       @"07/02/16",@"07/02/16",@"07/02/16",
                       @"07/03/16",@"07/03/16",
                       @"07/04/16"];

    self.datesArray = [[NSMutableArray alloc]init];

    [self.datesArray addObjectsFromArray:self.baseArray];
    [self.datesArray addObject:[NSNull null]];

    self.countArray = [[NSMutableArray alloc]init];

    int marker = 0;
    int counter = 0;

    //For loop for count of elements
for (int i = 0; i < self.datesArray.count; i++) {

    if (self.datesArray[marker] == self.datesArray[i]) {

        counter++;
        NSLog(@"MDate %@ Index %@ Counter: %d Marker: %d", self.datesArray[marker], self.datesArray[i], counter, marker);

    } else if (self.datesArray[marker] != self.datesArray[i]){

        [self.countArray addObject:[NSNumber numberWithInt:counter]];
        NSLog(@"Count is: %d", counter);

        marker = i;
        --i;
        counter = 0;
    } 
}
    NSLog(@"Count Array is: %@", self.countArray);
}

  @end
Robert
  • 63
  • 7
  • 1
    Just use two structures, an array to keep the order and a counted set to keep the counts. – Sulthan Jul 06 '16 at 18:52
  • 1
    You don't show how you "sort this back to out of the `NSCountedSet`", however there are plenty of ways to sort which take an ordering function (method, block) and you can provide one which sorts times with "am" before "pm" (primary key), and lower numbers before higher ones (secondary key). Or format your times using the 24 hour clock and only convert them to 12 hour on display/in your graph. HTH – CRD Jul 06 '16 at 19:03
  • @CRD interesting and simple idea. I'd still be stuck on the month sort but its a good start. I'll give it a shot tomorrow and see where it leads me. – Robert Jul 08 '16 at 00:52
  • @Sulthan great idea. I'm actually feeding the count and the order into two separate structures before it is displayed so I can just feed the counted set into the other. Though I'm not sure how it will preserve order of counts? I see how the array of dates will be preserved but still don't see how the counts would stay intact with the dates being in one array and the counts being in the other? – Robert Jul 08 '16 at 00:54
  • @CRD I updated the code to show the sort coming out of the counted set just a standard sort descriptor but I see what you mean about a compound of some kind. – Robert Jul 08 '16 at 00:59
  • 2
    Why not write code to do the task directly rather than try to build the solution out of sets and sorting? As shown your `datesForEvent` array is already in order, it is not hard to loop through this in order and reduce to an array of pairs (date, count). If `datesForEvent` is not ordered then its not much harder, e.g. the loop could do use an insertion sort and increment the count on a collision. – CRD Jul 08 '16 at 09:57
  • @CRD I had a few minutes to take a crack at it and your suggestion works, but my implementation seems a bit sloppy? Maybe not? It does what I need so thanks for help. I added it to the OP because it would be a mess otherwise. – Robert Jul 09 '16 at 21:44

1 Answers1

0

This is what I went with in the end.

    #import "ViewController.h"

    @interface ViewController ()

@property (nonatomic, strong) NSArray *baseArray;
@property (nonatomic, strong) NSMutableArray *datesArray;
@property (nonatomic, strong) NSMutableArray *countArray;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //Base Array of counts should be - 07/1 (4), 7/2 (3), 7/3 (2), 7/4 (1)
    self.baseArray = @[@"07/01/16",@"07/01/16",@"07/01/16",@"07/01/16",
                       @"07/02/16",@"07/02/16",@"07/02/16",
                       @"07/03/16",@"07/03/16",
                       @"07/04/16"];

    self.datesArray = [[NSMutableArray alloc]init];

    [self.datesArray addObjectsFromArray:self.baseArray];
    [self.datesArray addObject:[NSNull null]];

    self.countArray = [[NSMutableArray alloc]init];

    int marker = 0;
    int counter = 0;

    //For loop for count of elements
for (int i = 0; i < self.datesArray.count; i++) {

    if (self.datesArray[marker] == self.datesArray[i]) {

        counter++;
        NSLog(@"MDate %@ Index %@ Counter: %d Marker: %d", self.datesArray[marker], self.datesArray[i], counter, marker);

    } else if (self.datesArray[marker] != self.datesArray[i]){

        [self.countArray addObject:[NSNumber numberWithInt:counter]];
        NSLog(@"Count is: %d", counter);

        marker = i;
        --i;
        counter = 0;
    } 
}
    NSLog(@"Count Array is: %@", self.countArray);
}

@end
Robert
  • 63
  • 7