Intersecting two NSArrays with non-comparable objects inside - ios

The following code receives an array of events from EventKit for the selected day, then "pads" the existing calendar events with empty events to fill hourly slots from the entire day (9-5, for example).
The events coming from the iOS calendar are contained within a custom event container class that I created. The event class has the following properties:
// HDEvent.h
#interface HDEvent : NSObject
#property(nonatomic, copy) NSDate *startDate;
#property(nonatomic, copy) NSDate *endDate;
#property(nonatomic, strong) EKEvent *event;
#property(nonatomic, copy) NSString *eventIdentifier;
#property(nonatomic, copy) NSString *tempTitle;
#end
I am attempting to replace the empty events with any true events from the calendar, based on the start/end dates of the HDEvent object. The incoming array for the following method has the events (if any) from the device calendar.
- (NSArray *)addEmptyEventsWithEvents:(NSMutableArray *)events
ForDate:(NSDate *)date {
NSMutableOrderedSet *finalEvents = [[NSMutableOrderedSet alloc] init];
// this returns an array filled with the empty HDEvents
// for the expected date range.
NSArray *emptyEvents = [self getEventsTimeRangeFromDate:date];
for (HDEvent *emptyEvent in emptyEvents) {
HDEvent *eventToAdd;
for (HDEvent *event in events) {
NSLog(#"Event: %#", event.event.title);
if ([event.startDate isEqualToDate:emptyEvent.startDate] ||
[event.endDate isEqualToDate:emptyEvent.endDate])
{
eventToAdd = event;
} else {
eventToAdd = emptyEvent;
}
}
[finalEvents addObject:eventToAdd];
}
return [finalEvents array];
}
I am attempting to create an array with my events as illustrated in the pseudo-code below:
// Calendar Events
[
10:00-11:00 MEETING,
11:00-12:00 LUNCH
]
// Empty Placeholders
[
09:00-10:00 EMPTY,
10:00-11:00 EMPTY,
11:00-12:00 EMPTY,
12:00-01:00 EMPTY,
01:00-02:00 EMPTY,
02:00-03:00 EMPTY,
03:00-04:00 EMPTY,
04:00-05:00 EMPTY,
]
// Final desired result:
[
09:00-10:00 EMPTY,
10:00-11:00 MEETING,
11:00-12:00 LUNCH,
12:00-01:00 EMPTY,
01:00-02:00 EMPTY,
02:00-03:00 EMPTY,
03:00-04:00 EMPTY,
04:00-05:00 EMPTY,
]
[Problem]:
The problem is that the array returned only contains one event from the calendar (though I can see in the NSLog response that I have two events on that day)
If I swap the for loops around, then I get 9 events (again, only one from the calendar, and one duplicate empty event.)

Thank you #vikingosegundo.
I ended up implementing a custom comparison method in my class following the advice contained in this answer here: https://stackoverflow.com/a/877881/810360
I also extended it to be more flexible by utilizing the excellent DateTools by Matthew York found on GitHub: https://github.com/MatthewYork/DateTools
This was my final code:
-(BOOL)isEqualToEvent:(HDEvent*)event {
if (self == event) {
return YES;
}
if ([event.startDate isEqualToDate:self.startDate] &&
[event.endDate isEqualToDate:self.endDate]) {
return YES;
} else {
return NO;
}
}
-(BOOL)doesEventIntersectEvent:(HDEvent*)event {
DTTimePeriod *eventTimePeriod = [DTTimePeriod timePeriodWithStartDate:event.startDate endDate:event.endDate];
DTTimePeriod *selfTimePeriod = [DTTimePeriod timePeriodWithStartDate:self.startDate endDate:self.endDate];
if ([eventTimePeriod intersects:selfTimePeriod]) {
return YES;
} else {
return NO;
}
}
-(BOOL)doesEventContainEvent:(HDEvent*)event {
DTTimePeriod *eventTimePeriod = [DTTimePeriod timePeriodWithStartDate:event.startDate endDate:event.endDate];
DTTimePeriod *selfTimePeriod = [DTTimePeriod timePeriodWithStartDate:self.startDate endDate:self.endDate];
if ([eventTimePeriod contains:selfTimePeriod]) {
return YES;
} else {
return NO;
}
}

Related

Count number of times object appears in NSArray

I have an NSArray of 5 dice (dice1, dice2, dice3...). Once I have run the random number generator each dice1, dice2, dice3... can return a value between 1-6.
I would like to be able to count how many times a value of 1-6 has been returned.
I'm not too sure of the best way, whether I should turn the int number 1-6 into a string to match.
This is one of those situations where I don't feel that there is a particularly elegant solution due to the inability of Foundation types (such as NSCountedSet) to store intrinsic types (such as int). Swift's automatic boxing/unboxing of ints into NSNumber is a nice feature in this sort of situation.
Since you dealing with a small number of dice and a small number of possible values then you could ignore objects, sets and all that and just loop over your dice array, updating an array of integer counts.
The other, more complex, but object-oriented approach is to create a Die class:
Die.h
#import <Foundation/Foundation.h>
#interface Die : NSObject
-(instancetype)initWithSides:(NSUInteger)sides;
-(instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value;
-(NSUInteger)value;
-(NSUInteger)roll;
-(NSUInteger)sides;
#end
Die.m
#import "Die.h"
#interface Die ()
#property NSUInteger currentValue;
#property NSUInteger numberOfsides;
#end
#implementation Die
- (instancetype)initWithSides:(NSUInteger)sides {
NSAssert(sides>1, #"Dice must have at least 2 sides");
if (self = [super init]) {
self.numberOfsides = sides;
[self roll];
}
return self;
}
- (instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value {
NSAssert(sides>1, #"Dice must have at least 2 sides");
NSAssert(value <= sides, #"Initial value must not exceed number of sides");
if (self = [super init]) {
self.numberOfsides = sides;
self.currentValue = value;
}
return self;
}
- (NSUInteger)roll {
self.currentValue = arc4random_uniform((UInt32)self.numberOfsides)+1;
return self.currentValue;
}
- (NSUInteger)value {
return self.currentValue;
}
- (NSUInteger)sides {
return self.numberOfsides;
}
- (NSUInteger)hash {
return self.currentValue;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Die class]]) {
return NO;
}
return [self isEqualToDie:(Die *)object];
}
- (BOOL) isEqualToDie:(Die *)otherDie {
return self.currentValue == otherDie.value;
}
#end
So now you have an object that can be stored in an NSCountedSet and you can retrieve the counts. This bit is slightly awkward since you need to compare to a Die with the appropriate value, not just the value itself:
// self.dice is an array of `Die` objects
NSCountedSet *valueCounts = [NSCountedSet setWithArray:self.dice];
for (int i=1;i<7;i++) {
NSUInteger count = [valueCounts countForObject:[[Die alloc] initWithSides:6 initialValue:i]];
NSLog(#"There are %lu dice showing %d",count,i);
}
use dictionaries :
let arrNum = [“one”, “two”, “three”, “two”]
var countNumber:[String:Int] = [:]
for item in arrNum {
countNumber[item] = (countNumber[item] ?? 0) + 1
}
for (key, value) in countNumber {
print("\(key) occurs \(value) time")
}
o/p :
one occurs 1 time
two occurs 2 time
three occurs 1 time

Sync iPhone Calendar events deletion with events stored in app

I have one issue, if i adding event in calender through the app and if delete that added event from simuloter calendar delete button. Then it will also delete from app, is this possible?
Thanks
I am posting rough algorithm here to sync iCal events with the events stored in you app:
//model class for DB Events
#interface EventEntity
#property(strong) NSString *eventID;
#property(strong) NSString *eventText;
#end
NSMutableArray *dbEvents; //array of EventEntity objects
NSArray *iCalEvents; //array of events fetched from iCal
NSMutableArray *eventsToDelete = [NSMutableArray array]; //We will add events in this array, needed to be deleted from db.
for(EventEntity *entity in dbEvents) {
bool found = NO; //to keep track if this event has been found in iCal or not
for(EkEvent *event in iCalEvents) {
if([event.eventidentifier isEqualToString:entity.eventID]) {
found = YES; //event is present in Cal. Ignore and break
break;
}
}
if(!found) { //If not found, it means event has been deleted from iCal. Remove it from DB.
[eventsToDelete addObject:entity];
}
}
//Now delete items one by one from db
for(EventEntity *entity in eventsToDelete) {
//make query something like this: Delete event where EventID = entity.eventID
//after successful deletion, remove from dbEvents
[dbEvents removeObject:entity];
}
//refresh your UI for calendar/events if needed.
Hope it helps!

iOS: pointer reference to an array

I have a table view with a search, and search scope buttons with two possible scopes. The table is empty until a search is executed. Each scope has it's own mutable array for the table's data source, we'll say scopeA_array and scopeB_array. To simplify some methods, I'm trying to create a generic pointer reference to whichever array is the currently active scope. So I tried this:
#property (nonatomic, assign) NSMutableArray *tableDataArray;
In viewDidLoad, I assign it to the default selected scope.
_tableDataArray = _scopeA_array;
I can log the memory address of each array, they're both the same.
However, if I execute a search, _scopeA_array gets populated. Then in my numberOfRowsInSection method, I take the count of _tableDataArray but it's empty. I log the addresses again, both are different.
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
EDIT: A simplified way to test this, with the following lines of code, would like a way for tableDataArray to have the contents of testArray, even though the contents of testArray are assigned after:
NSArray *testArray = [NSArray new];
NSArray *tableDataArray = [testArray copy];
testArray = [NSArray arrayWithObjects:#"my", #"test", #"array", nil];
NSLog(#"table data array: %#", tableDataArray);
// logs empty array
I think the best approach is use a method to return conditionally the array for the current scope. So you just always use this method to populate your UITableView
- (NSMutableArray*) tableArray
{
return [self isScopeA] ? _scopeA_array : _scopeB_array;
}
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
If you want to track changes to a variable then you use a pointer to the variable rather than a pointer to a single array instance. E.g.:
#implementation MyController
{
__strong NSArray* *_currentDataPtr;
NSArray* _dataA;
NSArray* _dataB;
}
- (id)init
{
if (self = [super init])
{
_currentDataPtr = &_dataA; // Ensure _currentDataPtr is never NULL
}
return self;
}
- (void)setSearchScope:(NSInteger)searchScope
{
switch (searchScope)
{
default :
NSAssert(NO, #"");
case 0 :
_currentDataPtr = &_dataA;
break;
case 1 :
_currentDataPtr = &_dataB;
break;
}
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [*_currentDataPtr count];
}
If you want it to be a property then implement a property getter that dereferences the pointer:
#property (nonatomic, readonly) NSArray* currentData;
- (NSArray*)currentData { return *_currentDataPtr; }

iOS How do I compare if Objects are Equal? [duplicate]

This question already has answers here:
How do I compare objects in Objective-C?
(4 answers)
Closed 8 years ago.
I have a list of Objects that I pull from a web service. When I update my UITableView, I retrieve the objects again from the web service, and compare them to each other for equality. I then remove the ones that are not present, and insert the new objects, then update my UITableView. How can I test to see if the new object equals the old object? I've created a test for clarity..
requestA should equal requestC, but fails.
Is this possible to do without looking at each property value as the objects have many values?
I was originally comparing the ID only, but this doesn't work as sometimes other property values change and the ID stays the same.
Request *requestA = [[Request alloc] init];
Request *requestB = [[Request alloc] init];
Request *requestC = [[Request alloc] init];
requestA.requestID = #"1";
requestA.productName = #"Clutch";
requestB.requestID = #"2";
requestB.productName = #"Wiper";
requestC.requestID = #"1";
requestC.productName = #"Clutch";
if (requestA == requestB)
NSLog(#"A == B");
if (requestA == requestC)
NSLog(#"A == C");
if ([requestA isEqual:requestB])
NSLog(#"A isEqual B");
if ([requestA isEqual:requestC])
NSLog(#"A isEqual C");
// Look at the pointers:
NSLog(#"%p", requestA);
NSLog(#"%p", requestB);
NSLog(#"%p", requestC);
isEqual: is a method declared in NSObject Protocol. From official docs of isEqual:
This method defines what it means for instances to be equal. For
example, a container object might define two containers as equal if
their corresponding objects all respond YES to an isEqual: request.
See the NSData, NSDictionary, NSArray, and NSString class
specifications for examples of the use of this method.
If two objects are equal, they must have the same hash value. This
last point is particularly important if you define isEqual: in a
subclass and intend to put instances of that subclass into a
collection. Make sure you also define hash in your subclass.
Thus, as Salavat Khanov pointed out in his answer:
You need to implement -isEqual: and -hash methods for your Request class.
You want to do something like this:
// TestRequest.h
#interface TestRequest : NSObject
#property (nonatomic) NSString *requestID;
#property (nonatomic) NSString *productName;
#end
// TestRequest.m
#import "TestRequest.h"
#implementation TestRequest
- (BOOL)isEqual:(TestRequest *)object {
if (self == object) {
return YES;
}
if (![self.requestID isEqual:object.requestID]) {
return NO;
}
if (![self.productName isEqual:object.productName]) {
return NO;
}
return YES;
}
- (NSUInteger)hash {
// this is a very simple hash function
return [self.requestID hash] ^ [self.productName hash];
}
#end
or you can use a custom method:
- (BOOL)isEqualToRequest:(TestRequest *)otherRequest {
return [self.requestID isEqualToString:otherRequest.requestID] &&
[self.productName isEqualToString:otherRequest.productName];
}
Check this answer: How do I compare objects in Objective-C?
You need to implement -isEqual: and -hash methods for your Request class.
You need to overwrite isEqual: of your Request object to specify the properties to compare.
Write sth. like this:
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
if (!other || ![other isKindOfClass:[self class]]) return NO;
if (![(id)[self name] isEqual:[other name]]) return NO;
// add other checks if needed
return YES;
}
First off. == is a check for "are these two objects actually the SAME OBJECT". I.e. They are just two pointers to the same but of memory.
You need to be using the isEqual method. However, in order to do this properly you need to override the method in the class.
Something like...
- (BOOL)isEqual:(Request *)otherObject
{
return [self.productName isEqual:otherObject.productName]
&& [self.requestID isEqual:otherObject.requestID];
}

Removing an Observer when it gets Deallocated

I have the following 2 classes:
EventDispatcher:
#interface EventDispatcher()
-(id)initEventDispatcher;
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType;
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType;
#end
#implementation EventDispatcher
static EventDispatcher* eventDispatcher;
// Singleton.
+(EventDispatcher*)instance
{
if (eventDispatcher == nil)
{
eventDispatcher = [[EventDispatcher alloc] initEventDispatcher];
}
return eventDispatcher;
}
-(id)initEventDispatcher
{
self = [super init];
if (self)
{
eventSubscriptions = [[NSMutableDictionary alloc] init];
}
return self;
}
// Let anyone subscribe to an event. Return the EventSubscriber so they can dispatch events if needed, and to be able to unsubscribe.
-(EventSubscriber*)subscribe:(EVENT_TYPE)eventType :(void(^)(id package))operateEvent
{
// Create the object.
EventSubscriber* eventSubscriber = [[EventSubscriber alloc] initEventSubscriber:eventType :operateEvent];
// Now get the list it belongs to (we sort subscriptions in a dictionary so that when we dispatch an event, it's fast (we don't need to iterate through all EventSubscribers to find who subscribe to an event).
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if(subscriptionsToThisEvent == nil)
{
// If the list is nil, no one has subscribed to it yet, so make that list and add it to the dictionary.
subscriptionsToThisEvent = [[NSMutableArray alloc] init];
NSNumber* key = [self getKeyToEvent:eventType];
[eventSubscriptions setObject:subscriptionsToThisEvent forKey:key];
[subscriptionsToThisEvent release];
}
// Add the EventSubscriber to the subscription list.
[subscriptionsToThisEvent addObject:eventSubscriber];
[eventSubscriber release];
return eventSubscriber;
}
-(void)unsubscribe:(EventSubscriber*)eventSubscriber
{
// Get the list it belongs to, and remove it from that list.
EVENT_TYPE eventType = [eventSubscriber getEventType];
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if (subscriptionsToThisEvent != nil)
{
[subscriptionsToThisEvent removeObject:eventSubscriber];
}
}
-(void)dispatch:(EVENT_TYPE)eventType :(id)package
{
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
// If no one has subscribed to this event, it could be nil, so do nothing.
if (subscriptionsToThisEvent != nil)
{
// Otherwise, let them all know that the event was dispatched!
for (EventSubscriber* eventSubscriber in subscriptionsToThisEvent)
[eventSubscriber dispatch:package];
}
}
// Helper methods to get stuff (lists, keys) from event types.
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType
{
NSNumber* key = [self getKeyToEvent:eventType];
NSMutableArray* subscriptionsToThisEvent = [eventSubscriptions objectForKey:key];
return subscriptionsToThisEvent;
}
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType
{
return [NSNumber numberWithInt:eventType];
}
-(void)dealloc
{
[eventSubscriptions release];
[super dealloc];
}
#end
EventSubscriber:
#import "EventSubscriber.h"
#implementation EventSubscriber
-(id)initEventSubscriber:(EVENT_TYPE)newEventType :(void(^)(id package))newOperateEvent
{
self = [super init];
if (self)
{
operateEvent = [newOperateEvent copy];
eventType = newEventType;
}
return self;
}
-(void)dispatch:(id)package
{
operateEvent(package);
}
-(EVENT_TYPE)getEventType
{
return eventType;
}
-(void)dealloc
{
[operateEvent release];
[super dealloc];
}
#end
Onto the big question: How do I unburden a programmer who is using this system with having to unsubscribe from an event during deallocation? When multiple classes are using this system, programmers will have to remember to unsubscribe during deallocation (if not an earlier time), or REALLY bad/weird/unexpected things could happen (I would prefer a compile-time check, or a big, obvious, debuggable crash, but more-so the former). Ideally, I'd like to restructure this system (or do anything) so that when an object is deallocated, the EventDispatcher gracefully handles it.
One quick fix is to have objects allocate EventSubscribers directly, then in the EventSubscriber constructor, it subscribes itself to EventDispatcher (but that's obviously bad, maybe make EventDispatcher's stuff static? Ugh now we're just getting worse).
Side notes:
I'm not using ARC, but, that does not matter here (at least I think it does not, if there are ARC-based solutions, I'd like to hear them).
I do plan on adding a method in EventDispatcher to be able to remove EventSubscribers by those who did the subscription (so now when subscribing, objects will have to pass 'self'). I also plan on making the enumerated EVENT_TYPE use strings, but that's a different topic altogether.
I also plan on translating a lot of my code (including these classes) to C++. So I'd appreciate a conceptual solution as opposed to Objective-C specific solutions.
So, is this possible?
Thanks a bunch!

Resources