Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Anim

macrumors 6502a
Original poster
Dec 16, 2011
616
25
Macclesfield, UK
Hey all

I am pulling my hair out trying to work out how to convert a category list into a data model for a NSOutlineView.

I am attempting to create a Source view / NSOutlineView to let a user select a product category. Just like a tree view. There are no Leaf nodes, just roots and branches.

The user entered data is stored in an SQLite table as the following:

Code:
ID           PRODUCT         PARENTID
1            FORD            0
2            HONDA           0
3            FIESTA          1
4            XR2             3
5            ESCORT          1
6            CIVIC           2
7            TURBO           6

Three could be 20 or 1000 of these records, the user is allowed to add and delete them.

So we can see there are 2 root nodes (Ford, Honda) and various branches and branches of branches (Turbo, XR2)

So, for the data model I created a class to hold the data as in:
Code:
#import <Foundation/Foundation.h>

@interface BP_category : NSObject

@property (copy) NSString *categoryName;
@property (readonly, copy) NSMutableArray *subCategory;

-(id)initWithName:(NSString *)name;
-(void)addChildCategory:(BP_category *)c;

@end

------------------

#import "BP_category.h"

@implementation BP_category

-(id)init {
    return [self initWithName:@"Untitled"];
}

-(id)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        _categoryName = [name copy];
        _subCategory  = [[NSMutableArray alloc]init];
    }
    
return self;
}

-(void)addChildCategory:(BP_category *)c {
    [_subCategory addObject:c];
}


@end

What I can't figure out is how to traverse the SQLite data and store it in the class object. I get muddled up when it comes to children of children.

Any help would be much appreciated.

I am also using FMDB as an SQLite wrapper.

Many thanks
Anim
 
I can't help you with the cocoa syntax, but I can help from a general programming point of view.

First you will need to add Id as a property of BP_Category

Next you will need to do something like the following. Note that I dont know how sql access works in cocoa, I'm assuming (possible incorrectly) that it is vaguely similar to how .net works

Code:
-(NSMutableArray*) getCategoriesFromSql()
{
	//create MySQL connection
	//Create Command object
	//execute SQL query, get a MySqlReader
	
	//This list is a temporary list used for looking up parent categories
	NSMutableArray* listCategories = [[NSMutableArray alloc] init];
	
	//This list is for holding the top level categories
	NSMutableArray* listTopeLevelCategories = [[NSMutableArray alloc] init];
	
	while(reader.read())
	{
		int id = reader["ID"];
		NSString* product = reader["Product"];
		int parentId = reader["ParentID"];
		
		//create object for current record
		BP_category* newCategory = [[BP_Category alloc]initWithName:product withId:id]
		
		//search to see if parent exists
		BP_category* parentCategory = nil;
		foreach(BP_category* category in listCategories)
		{
			if(cat.id == parentId)
			{
				parentCategory = cat;
				break;
			}
		}
		
		//if a parent was found, add the new category as a child
		//otherwise, add it to the top level list
		if(parentCategory)
		{
			[parentCategory addChildCategory:newCategory];
		}
		else
		{
			[listTopeLevelCategories addObject:newCategory];
		}
		
		//add the new category to our lookup list
		[listCategories addObject:newCategory];
	}
	
	return listTopeLevelCategories;
}
 
Interesting, thanks. I will convert that and see how it functions. My issue was when adding a child of a child of a root to the array and where i get confused if there are say 10 child records of a particular root object. I knew I needed a loop within a loop to locate child categories but it was the storing of each parent and accessing a parent (so I can add a child to it) later in the record set.

But, I will take a look at this and see how it works.

Thanks again
Anim
 
Another possible solution I found earlier was to use Cocoa bindings and an NSDictionary object which works well with NSOutlineView's and a NSTreeController for managing it.

As shown here: http://daemonconstruction.blogspot.co.uk/2012/03/simplest-nstreecontroller-example.html

I can then just work with a Dictionary object.

I will try both methods and time them on 1000 records to see which is faster, an array of class objects vs a Dictionary for the data model.

Cheers
Anim
 
Mine isn't super optimized. For the lookup list you'd be better off using a dictionary or some other container to look up the categories directly by Id, rather than looping through the entire list trying to find it.

Good luck, either way.
 
When I'm prototyping schemas I'll create wrapper classes around NSDictionaries and convert them into a proper implementation once my schema is set. Maybe it's my C#/Perl background coming out but it also lends itself to emulating C#'s named parameters/Perl object constructors.

I.e.

Code:
MyClass *obj = [[MyClass alloc] initWithDictionary:@{
                          @"name"     : someVar1, 
                          @"tag"         : someVar1, 
                          @"location"  : someVar1, 
                          @"time"       : someVar1, 
                       }];
 
When I'm prototyping schemas I'll create wrapper classes around NSDictionaries and convert them into a proper implementation once my schema is set. Maybe it's my C#/Perl background coming out but it also lends itself to emulating C#'s named parameters/Perl object constructors.

I.e.

Code:
MyClass *obj = [[MyClass alloc] initWithDictionary:@{
                          @"name"     : someVar1, 
                          @"tag"         : someVar1, 
                          @"location"  : someVar1, 
                          @"time"       : someVar1, 
                       }];

Erk. I think it was Ben Franklin who said:
"He who would give up compile-time type safety and requiredness for convenience deserves neither."

-Lee
 
Erk. I think it was Ben Franklin who said:
"He who would give up compile-time type safety and requiredness for convenience deserves neither."

-Lee

He also came up with Daylight Savings Time. Not all of his ideas were good in the long run ;).

The key word there is "prototyping". Having a class to search for is a lot nicer than grepping for "theNameOfSomeRandomDictionaryImKillingMySelfOver".

After I'm done prototyping the class can easily turn into

Code:
[[MyClass alloc] initWithName: someVar1
                          tag: someVar2, 
                          location: someVar3];
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.