Subdividng an Array of Core data objects by attribute - objective-c

I have an array of core data objects that have a time(date) attribute.
What I want to do is place all the core data objects that have the same time into separate arrays and then create an array of those arrays. If no other object has the same time then that object will be in an array by itself.
i.e If the array of core data objects had 3 objects with time 08:00 and 1 object with time attribute 09:00 then I want to create one array with the first 3 objects(time 08:00) and a separate array with the last object (time 09:00). Then I want to create an array of those arrays which should be easy enough. Start: [8,8,8,9] Finish: [[8,8,8][9]]
I am struggling most with how to iterate through my original array and pull out all the objects with the same time and then put those objects in their own array.

One of many ways to do this is using a NSDictionary object where the time is a key and the corresponding object is an array of core data objects that have this time. The core data objects are iterated once.
NSArray *data = core data objects
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
for (ManagedObjectClass *object in data) {
NSDate *time = object.time;
// convert time to time without date
NSMutableArray *array = dictionary[time];
if (!array) {
array = [NSMutableArray array];
dictionary[time] = array;
}
[array addObject:object];
}
NSArray *array = dictionary.allValues;
Another way to do this if the time doesn't need conversion: sort the core data objects by time, iterate the objects, start a new subarray when the time changes and add the object to the subarray.
NSArray *data = core data objects
NSArray *sortedData = [data sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES]]];
NSMutableArray *array = [NSMutableArray array];
NSMutableArray *subarray = nil;
NSDate *prevTime = nil;
for (ManagedObjectClass *object in sortedData) {
NSDate *time = object.time;
if (![time isEqualToDate:prevTime]) {
subarray = [NSMutableArray array];
[array addObject:subarray];
}
[subarray addObject:object];
prevTime = time;
}

You can do some thing like this:
Add a transient property in your coredata object.
Eliminate the minutes and seconds from your date in transient property.
Get distinct objects based on your transient property.
Filter objects from your main array and add them in a separate array.
Finally add them in final array.
Implementation can be like this:
MyObject *myObject1 = [[MyObject alloc] initWithDate:[NSDate dateWithTimeIntervalSinceNow:10*60]];
MyObject *myObject2 = [[MyObject alloc] initWithDate:[NSDate dateWithTimeIntervalSinceNow:10*60]];
MyObject *myObject3 = [[MyObject alloc] initWithDate:[NSDate dateWithTimeIntervalSinceNow:180*60]];
MyObject *myObject4 = [[MyObject alloc] initWithDate:[NSDate dateWithTimeIntervalSinceNow:120*60]];
MyObject *myObject5 = [[MyObject alloc] initWithDate:[NSDate dateWithTimeIntervalSinceNow:120*60]];
NSMutableArray *myObjects = [NSMutableArray arrayWithObjects:myObject1,myObject2,myObject3,myObject4,myObject5, nil];
NSArray *distinctObjects = [myObjects valueForKeyPath:#"#distinctUnionOfObjects.transientDate"];
NSMutableArray *finalArray = [NSMutableArray array];
for(NSDate *aDate in distinctObjects) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"transientDate == %#", aDate];
NSArray *filteredArray = [myObjects filteredArrayUsingPredicate:predicate];
[finalArray addObject:filteredArray];
}
NSLog(#"distintobjects: %#", finalArray);
Here is how MyObject looks like:
#import <Foundation/Foundation.h>
#interface MyObject : NSObject
{
}
#property(nonatomic, strong) NSDate *myDate;
#property(nonatomic, strong) NSDate *transientDate;
- (id)initWithDate:(NSDate *)aDate;
#end
#import "MyObject.h"
#implementation MyObject
- (id)initWithDate:(NSDate *)aDate {
self = [super init];
self.myDate = aDate;
return self;
}
- (NSDate *)transientDate {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy HH"];
return [dateFormatter dateFromString:[dateFormatter stringFromDate:self.myDate]];
}
- (NSString *)description
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy HH:mm"];
return [NSString stringWithFormat:#"transientDate: %#", [dateFormatter stringFromDate:self.transientDate]];
}
#end
Here is the final output:

Related

Default list in Objective C?

I want to store data at run time, I can have a linked list and add at runtime, however as I am new to IOS and objective C, do we have any default list in which we can add our datas, (Datas are two stings and an integer).
Cocoa provides NSArray and NSMutableArray, a pair of ordered containers similar to Java's ArrayList and C#'s List. You can add values to NSMutableArray, and it will grow as you add more elements; NSArray is read-only.
You can use .plist files to store your data.
Read more 'Loading data from .plist files', 'How to use plist in iphone?' or google it like 'load data from .plist'. However you can create NSArray or something like this at run time. If you want dive deeper you must read ObjC Collections Programming Topics
Make a class for your data with default properties and make sure it inherits NSObject then use NSMUtableArray to add/remove elements to the list.
// in the .h file of your object
#interface MyObject : NSObject {
NSString* strAttribute1;
// add more attributes as you want
}
#property (nonatomic, retain) NSString* strAttribute1;
#end
// then in the .m file
// do not forget the #import ""
#implement MyObject
#synthesize strAttribute1;
// override the dealloc to release the retained objects
#end
then in your code where you want to make a list of this object
NSMutableArray* myArray = [[NSMutableArray alloc] init];
// add elements and iterate through them
// do not forgot to free the memory if you are not using ARC
[myArray release];
You can use NSArray or NSMutableArray or NSDictionary or NSMutableDictionary depending on your needs.
NSArray:
NSArray *myArray;
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = #"a string";
myArray = [NSArray arrayWithObjects:aDate, aValue, aString, nil];
NSMutableArray:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = #"a string";
[myArray addObject:aDate];
[myArray addObject:aValue];
[myArray addObject:aString];
NSDictionary:
NSDictionary * myDict = [NSDictionary dictionaryWithObjects:aDate, aValue, aString forKeys:firstDate, firstValue, firstString];
NSMutableDictionary:
NSString *aString = #"a string";
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
myDict = [[NSMutableDictionary alloc] init];
[myDict setObject:aString forKey:firstString];
[myDict setObject:aDate forKey:firstDate];
[myDict setObject:aValue forKey:firstValue];

Fast way to search the properties of objects in an NSArray

I have an NSArray of custom objects that all have a #property name of type NSString. How can I quickly enumerate through the array and create a new array that contains only the objects that have a specific word in their name property?
For instance:
CustomObject *firstObject = [[CustomObject alloc] init];
firstObject.name = #"dog";
CustomObject *secondObject = [[CustomObject alloc] init];
secondObject.name = #"cat";
CustomObject *thirdObject = [[CustomObject alloc] init];
thirdObject.name = #"dogs are fun";
NSMutableArray *testArray = [NSMutableArray arrayWithObjects:firstObject,
secondObject,
thirdObject,
nil];
// I want to create a new array that contains all objects that have the word
// "dog" in their name property.
I know I could use a for loop like so:
NSMutableArray *newArray = [NSMutableArray array];
for (CustomObject *obj in testArray)
{
if ([obj.name rangeOfString:#"dog"].location == NSNotFound) {
//string wasn't found
}
else {
[newArray addObject:obj];
}
}
But is there a more efficient way? Thanks!
NSString *searchString = #"dog";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains %#", searchString];
NSArray *filteredArray = [testArray filteredArrayUsingPredicate:predicate];
Please have a look at NSPredicates ! They are highly efficient when you are searching / filtering through array results. This is the documentation!

Different Keys Point to Same Object(s) in NSMutableDictionary

I have a custom object called Person that among other things contains an NSString field called descriptor, which stores what sort of person that Person object is (angry, sad, wild, happy, morose, etc). All of my Person objects are in an NSMutableArray, but I would like to store them in an NSMutableDictionary in such a manner:
Key: A, Object: An NSMutableArray where all Person objects have descriptor starting with 'A'
Key: B, Object: An NSMutableArray where all Person objects have descriptor starting with 'B'
Key: C, Object: An NSMutableArray where all Person objects have descriptor starting with 'C'
etc...
I've tried to do this in my code below, and at the comment //POINT 1, the keys and arrays seem to match up, but at //POINT 2, when I print out the complete dictionary, all the keys come up with the same values!
So I wanted to know why the NSMutableArray I seem to have is not being stored as I want it in the NSMutableDictionary?
- (void)buildDictionaryForIndexList {
NSMutableDictionary *tempDict = [[[NSMutableDictionary alloc] init] autorelease];
NSMutableArray *personsStartingWithLetter = [[NSMutableArray alloc] init];
NSMutableArray *indexList = [[[NSMutableArray alloc] init] autorelease];
NSInteger loopCounter = 1;
NSString *firstLetter = [[[NSString alloc] init] autorelease];
for (Person *v in persons) {
firstLetter = [[v descriptor] substringWithRange:NSMakeRange(0, 1)];
if ([indexList containsObject:firstLetter]) {
[personsStartingWithLetter addObject:v];
if (loopCounter == [persons count]) {
[tempDict setObject:personsStartingWithLetter forKey:firstLetter];
}
} else {
if (loopCounter > 1) {
//POINT 1
NSLog(#"%#",[indexList objectAtIndex:[indexList count]-1]);
for (Person *q in personsStartingWithLetter) {
NSLog(#"%#",[q descriptor]);
}
[tempDict setObject:personsStartingWithLetter forKey:[indexList objectAtIndex:([indexList count] - 1)]];
[personsStartingWithLetter removeAllObjects];
}
[indexList addObject:firstLetter];
[personsStartingWithLetter addObject:v];
} // else
loopCounter++;
} // for
//POINT 2
NSEnumerator *enumerator = [tempDict keyEnumerator];
for (NSString *str in enumerator) {
NSLog(#"%#",str);
for (Person *c in [tempDict objectForKey:str]) {
NSLog(#"%#",[c descriptor]);
}
}
self.dictionary = tempDict;
} // buildDictionaryForIndexList
So, for example, at POINT 1 my output is:
A
Angry
Amiable
B
Belligerent
C
Cool
...
W
Wild
but at POINT 2 my output is
T
Wild
J
Wild
A
Wild
...
W
Wild
Change [tempDict setObject:personsStartingWithLetter forKey:[indexList objectAtIndex:([indexList count] - 1)]]; (just after point 1) to [tempDict setObject:[[personsStartingWithLetter copy] autorelease] forKey:[indexList objectAtIndex:([indexList count] - 1)]];. The problem is that NSDictionary copies the key, but retains the value. Therefore, if you add a mutable array to the dictionary and then change it, the array in the dictionary also changes. You need to create a non-mutable copy of the array to put in the dictionary.
The whole method is a bit overcomplicated.
- (void)buildDictionaryForIndexList
{
NSMutableDictionary *tempDict = [[[NSMutableDictionary alloc] init] autorelease];
for (Person *v in persons)
{
NSString* firstLetter = [[v descriptor] substringWithRange:NSMakeRange(0, 1)];
NSMutableArray* personsStartingWithLetter = tempDict [firstLetter];
if (personsStartingWithLetter == nil)
{
personsStartingWithLetter = [NSMutableArray array];
tempDict [firstLetter] = personsStartingWithLetter;
}
[personsStartingWithLetter addObject:v];
} // for
self.dictionary = tempDict;
}
You start with an empty dictionary that will contain arrays. For every person, you check whether there is a suitable array or not, and if there isn't one, you create it. So now there is an array for the person, so you add it to the array. That's all.

objective-c spilt array in multiple arrays for uitableview grouped

hi i have an array of objects which need to be sorted (alphabet on name)
ArtistVO *artist1 = [ArtistVO alloc];
artist1.name = #"Trentemoeller";
artist1.imgPath = #"imgPath";
ArtistVO *artist2 = [ArtistVO alloc];
artist2.name = #"ATrentemoeller";
artist2.imgPath = #"imgPath2";
ArtistVO *artist3 = [ArtistVO alloc];
artist3.name = #"APhextwin";
artist3.imgPath = #"imgPath2";
//NSLog(#"%#", artist1.name);
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:artist1];
[arr addObject:artist2];
[arr addObject:artist3];
NSSortDescriptor *lastDescriptor =
[[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray * descriptors =
[NSArray arrayWithObjects:lastDescriptor, nil];
NSArray * sortedArray =
[arr sortedArrayUsingDescriptors:descriptors];
NSLog(#"\nSorted ...");
NSEnumerator *enumerator;
enumerator = [sortedArray objectEnumerator];
ArtistVO *tmpARt;
while ((tmpARt = [enumerator nextObject])) NSLog(#"%#", tmpARt.name);
works fine. but now i need to split this awway up for usage in a grouped uitableview
self.sortedKeys =[[NSArray alloc]
initWithObjects:#"{search}",#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K",#"L",#"M",#"N",#"O",#"P",#"Q",#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z",nil];
NSMutableArray *arrTemp0 = [[NSMutableArray alloc]
initWithObjects:#"000",nil];
NSMutableArray *arrTemp1 = [[NSMutableArray alloc]
initWithObjects:#"Andrew",#"Aubrey",#"Aalice", #"Andrew",#"Aubrey",#"Alice",#"Andrew",#"Aubrey",#"Alice",nil];
NSMutableArray *arrTemp2 = [[NSMutableArray alloc]
initWithObjects:#"Bob",#"Bill",#"Bianca",#"Bob",#"Bill",#"Bianca",nil];
NSMutableArray *arrTemp3 = [[NSMutableArray alloc]
initWithObjects:#"Candice",#"Clint",#"Chris",#"Candice",#"Clint",#"Chris",nil];
NSMutableArray *arrTemp4 = [[NSMutableArray alloc]
initWithObjects:#"Dandice",#"Dlint",#"Dhris",nil];
NSDictionary *temp =[[NSDictionary alloc]
initWithObjectsAndKeys:arrTemp0, #"{search}", arrTemp1,#"A",arrTemp2,
#"B",arrTemp3,#"C",arrTemp4,#"D",nil];
self.tableContents =temp;
so all Artist with first letter "a" come in one array ... with "b" in one array and so on.
do i need to do some string comparism or is there a better approach?
How about:
Create an empty NSMutableDictionary.
Loop through all your strings. For each string:
If string is empty, ignore this string.
Get a NSString containing first character of the string converted to uppercase. This will be your dictionary key.
Look in the dictionary to see if it already contains this key.
If not, create a new NSMutableArray containing just your string and add it as the value to this new key.
If so, add this string to the end of the existing array.
End of loop.

Fill NSMutableArray from another NsMutableArray

I have two classes indexViewController and flashCardQuestionViewController.
In the indexViewController i have table filled with an array.
Now i am getting some data from the database:
-(void)getMultipleChoiceAnswer
{
if(optionid!=nil)
[optionid removeAllObjects];
else
optionid = [[NSMutableArray alloc] init];
if(optionText!=nil)
[optionText removeAllObjects];
else
optionText = [[NSMutableArray alloc] init];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:"select optionID,OptionText from flashCardMultipleAnswer where questionId=1"];
while(sqlite3_step(dataRows) == SQLITE_ROW)
{
[optionid addObject:[NSNumber numberWithInt:sqlite3_column_int(dataRows,0)]];
[optionText addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)]];
}
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
and I am calling this method in the viewDidLoad method of the indexViewController.
Now I have another NSMutableArray in the flashCardQuestionViewController named listNoOfOptionsInQuestion.
I want to fill listNoOfOptionsInQuestion with objects from optionText array in indexViewController.
How can I do this?
There are a number of ways to copy arrays: you can either use -[NSArray copy] to get an immutable copy, or -[NSArray mutableCopy] for a mutable copy. Don't forget that copy adds a reference so you'll need a release or autorelease somewhere (if you're not using GC that is).
Alternatively, you can use -[NSMutableArray addObjectsFromArray:].
Given your example, it looks like you want to do something like this at the end:
[flashCardQuestionViewController setListNoOfOptionsInQuestion:optionText];
And then in FlashCardQuestionViewController, you want something like:
- (void)setListNoOfOptionsInQuestion:(NSArray *)options
{
if (options != listNoOfOptionsInQuestion) {
[listNoOfOptionsInQuestion release];
listNoOfOptionsInQuestion = [options mutableCopy];
}
}
Rahul,
Do you really need to have a completely different copy of the MutableArray in each object. Would it be possible to have both objects point to the same array? For instance:
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = mArray;
Or do you need to make changes to the two arrays in different ways? The try this (as suggested by Chris above) :
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = [mArray mutableCopy];
again, if this isn't what you need then you'll have to give us a more precise question or problem that we can identify.