I have a large NSDictionary. Fx.
"m:GetFolderResponse" = {
"m:ResponseMessages" = {
"m:GetFolderResponseMessage" = {
ResponseClass = Success;
"m:Folders" = {
"t:CalendarFolder" = {
"t:ChildFolderCount" = {
text = 0;
};
"t:DisplayName" = {
text = Calendar;
};
"t:FolderId" = {
ChangeKey = "AgAAABYAAABGewbOYWpKSrW/k23iIoFkAPJWd7/8";
Id = "AAMkADkwOWE2NjEyLTMwZWQtNGYyMy05OGQ1LWZjZjFkZGY5MTBhMAAuAAAAAAC1cjo8jkv5SKjQt5WaSmd1AQBGewbOYWpKSrW/k23iIoFkAPJWc0NrAAA=";
};
};
};
"m:ResponseCode" = {
text = NoError;
};
};
};
"xmlns:m" = "http://schemas.microsoft.com/exchange/services/2006/messages";
"xmlns:t" = "http://schemas.microsoft.com/exchange/services/2006/types";
};
}
As you might have guessed, there can be multiple in the m:Folders. Therefore I would like to find m:Folders child, where t:DisplayName is equal to a variable value. How can I do this?
- (void)filterMutableDictionary:(NSDictionary*)aDictionary andKeyName:(NSString *)keyName
{
if ([keyName isEqualToString:#"t:CalendarFolder"]) {
if ([[[aDictionary objectForKey:#"t:DisplayName"] objectForKey:#"text"] isEqualToString:searchCalendarName]) {
NSDictionary *temp = [aDictionary objectForKey:#"t:FolderId"];
CalID = [temp objectForKey:#"Id"];
CalChangeID = [temp objectForKey:#"ChangeKey"];
}
}
// enumerate key/values, filtering appropriately, recursing as necessary
NSLog(#"%#",aDictionary);
[aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
if ([value isKindOfClass: [NSMutableDictionary class]] || [value isKindOfClass: [NSDictionary class]]) {
[self filterMutableDictionary: value andKeyName:key];
}
}];
}
Related
I have a model translator. It reads AModel's properties, and copy the value to BModel's same property if BModel has. Now I have a crash report shows the property is null. That is so strange. The property is got from a property list and it is null.
Here is the message:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<XXXXXX 0x1594051a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key (null).
+ (instancetype)convertSourceObject:(id)sourceObject {
if (!sourceObject) {
return nil;
}
id destination_object = [[self alloc] init];
uint destination_properties_count = 0;
objc_property_t *destination_properties = class_copyPropertyList([self class], &destination_properties_count);
for (int i = 0; i < destination_properties_count; i++) {
objc_property_t destination_property = destination_properties[i];
uint source_attributes_count = 0, destination_attributes_count = 0;
objc_property_attribute_t *destination_attributes = property_copyAttributeList(destination_property, &destination_attributes_count);
const char *property_char_name = property_getName(destination_property);
NSString *property_name = [NSString stringWithUTF8String:property_char_name];
objc_property_t source_property = class_getProperty([sourceObject class], property_char_name);
if (source_property && ![ignorePropertyNames() containsObject:property_name]) {
objc_property_attribute_t *source_attributes = property_copyAttributeList(source_property, &source_attributes_count);
NSString *source_ivar_type = #"";
NSString *destination_ivar_type = #"";
for (int i = 0; i < source_attributes_count; i++) {
if (strcmp(source_attributes[i].name, "T") == 0) {
source_ivar_type = [[NSString stringWithUTF8String:source_attributes[i].value] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"#\""]];
break;
}
}
for (int i = 0; i < destination_attributes_count; i++) {
if (strcmp(destination_attributes[i].name, "T") == 0) {
destination_ivar_type = [[NSString stringWithUTF8String:destination_attributes[i].value] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"#\""]];
break;
}
}
if ([self isPropertySetType:source_ivar_type]) {
id source_value = [sourceObject valueForKey:property_name];
if ([source_value isKindOfClass:[NSArray class]]) {
NSArray *destination_array = [self arrayConvert:(NSArray*)source_value];
if (destination_array) {
[destination_object setValue:destination_array forKey:property_name];
}
} else if ([source_value isKindOfClass:[NSDictionary class]]) {
NSDictionary *destination_dict = [self dictionaryConvert:(NSDictionary *)source_value];
if (destination_dict) {
[destination_object setValue:destination_dict forKey:property_name];
}
}
} else {
if ([destination_ivar_type isEqualToString:source_ivar_type]) {
id source_value = [sourceObject valueForKey:property_name];
if (source_value) {
[destination_object setValue:source_value forKey:property_name];
}
} else {
id source_value = [sourceObject valueForKey:property_name];
id destination_value = [NSClassFromString(destination_ivar_type) convertSourceObject:source_value];
if (destination_value) {
[destination_object setValue:destination_value forKey:property_name];
}
}
}
free(source_attributes);
} else {
continue;
}
free(destination_attributes);
}
free(destination_properties);
return destination_object;
}
The error means: you try to read a property which name's "null" AModel's property , but it doesn't exit in AModel.
You should overwrite valueForUndefinedKey in your Model Class, debug the UndefinedKey.
Check your code.It seems happened at
id source_value = [sourceObject valueForKey:property_name];
NSLog debug the property_name, see what you got.
I am storing my json response in a NSDictionary and this dictionary contains various array, so I want to replace all the null values with #"" empty string.
{
Specialities = (
{
ApprovalStatus = Unapproved;
CurrencyCode = "<null>";
Packages = (
{
AssetId = 157965;
BasePrice = 10000;
BookingAdvance = 100;
Currency = INR;
Details = "<null>";
DiscountedPrice = 9000;
Id = 16579;
IsBestOffer = 1;
IsPopular = 1;
LineItems = (
{
IconClass = "fa-check";
Text = "A DVD with all edited and unedited images";
}
);
PackageVersion = 123955;
PriceUnit = 3;
Quantity = 4;
SpecialityId = 22;
Status = Rejected;
Tags = (
53
);
TermsAndConditions = "<null>";
Title = Test;
}
);
Photos = (
157965,
157964
);
ServiceDescription = 43534;
Speciality = 22;
SpecialityName = "Wedding Photographer";
UserFRPs = (
{
AssetId = 157965;
CurrencyCode = INR;
DiscountedPrice = 800;
FRPId = 13;
Id = 4559;
Price = 1000;
SpecialityId = 22;
Status = Active;
},
{
AssetId = 565441;
CurrencyCode = INR;
DiscountedPrice = 9000;
FRPId = 18;
Id = 5559;
Price = 10000;
SpecialityId = 22;
Status = Active;
}
);
Videos = (
{
VideoId = DaWOguXZbNA;
VideoLink = "http://www.youtube.com/watch?v=DaWOguXZbNA";
VideoType = YouTube;
},
{
VideoId = DGVJtAHzzDQ;
VideoLink = "http://www.youtube.com/watch?v=DGVJtAHzzDQ";
VideoType = YouTube;
},
{
VideoId = "_zxKLZR-xuk";
VideoLink = "http://www.youtube.com/watch?v=_zxKLZR-xuk";
VideoType = YouTube;
},
{
VideoId = 5SkBZcvuuQs;
VideoLink = "http://www.youtube.com/watch?v=5SkBZcvuuQs";
VideoType = YouTube;
},
{
VideoId = "H_Xi-lVB4Zw";
VideoLink = "http://www.youtube.com/watch?v=H_Xi-lVB4Zw";
VideoType = YouTube;
},
{
VideoId = TWhSjpsGvPQ;
VideoLink = "http://www.youtube.com/watch?v=TWhSjpsGvPQ";
VideoType = YouTube;
},
{
VideoId = N2CJrhHEydA;
VideoLink = "http://www.youtube.com/watch?v=N2CJrhHEydA";
VideoType = YouTube;
},
{
VideoId = Lq6faQVYcwY;
VideoLink = "http://www.youtube.com/watch?v=Lq6faQVYcwY";
VideoType = YouTube;
},
{
VideoId = v8WjMiodcKo;
VideoLink = "http://www.youtube.com/watch?v=v8WjMiodcKo";
VideoType = YouTube;
}
);
},
{
ApprovalStatus = Unapproved;
CurrencyCode = "<null>";
Packages = "<null>";
Photos = (
157967
);
ServiceDescription = Ddhd;
Speciality = 37;
SpecialityName = "Hair and Makeup Stylist";
UserFRPs = (
{
AssetId = 157967;
CurrencyCode = INR;
DiscountedPrice = 900;
FRPId = 34;
Id = 4560;
Price = 1000;
SpecialityId = 37;
Status = Active;
}
);
Videos = (
{
VideoId = "onvkllwM-OI";
VideoLink = "http://www.youtube.com/watch?v=onvkllwM-OI";
VideoType = YouTube;
},
{
VideoId = "_-cRVdTW2s8";
VideoLink = "http://www.youtube.com/watch?v=_-cRVdTW2s8";
VideoType = YouTube;
},
{
VideoId = DGVJtAHzzDQ;
VideoLink = "http://www.youtube.com/watch?v=DGVJtAHzzDQ";
VideoType = YouTube;
}
);
},
{
ApprovalStatus = Unapproved;
CurrencyCode = "<null>";
Packages = "<null>";
Photos = (
157963,
157962,
157961
);
ServiceDescription = Test;
Speciality = 55;
SpecialityName = Transport;
UserFRPs = "<null>";
Videos = (
{
VideoId = "cRchvv_dB2c";
VideoLink = "http://www.youtube.com/watch?v=cRchvv_dB2c";
VideoType = YouTube;
},
{
VideoId = "onvkllwM-OI";
VideoLink = "http://www.youtube.com/watch?v=onvkllwM-OI";
VideoType = YouTube;
},
{
VideoId = DGVJtAHzzDQ;
VideoLink = "http://www.youtube.com/watch?v=DGVJtAHzzDQ";
VideoType = YouTube;
}
);
},
{
ApprovalStatus = Unapproved;
CurrencyCode = "<null>";
Packages = "<null>";
Photos = "<null>";
ServiceDescription = Baby;
Speciality = 5;
SpecialityName = "Children/Babies Photographer";
UserFRPs = "<null>";
Videos = (
{
VideoId = "cRchvv_dB2c";
VideoLink = "http://www.youtube.com/watch?v=cRchvv_dB2c";
VideoType = YouTube;
},
{
VideoId = DGVJtAHzzDQ;
VideoLink = "http://www.youtube.com/watch?v=DGVJtAHzzDQ";
VideoType = YouTube;
}
);
},
}
I want all null values to be replace by empty string.
Then just do that
NSString *json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:yourDictionary options:0 error:nil] encoding: NSUTF8StringEncoding];
NSString *jsonWithoutNulls = [json stringByReplacingOccurrencesOfString:#"<null>" withString:#""];
NSData *data = [jsonWithoutNulls dataUsingEncoding:NSUTF8StringEncoding]
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
I prefer to use Macros to determine null values.
#define IS_NULL(value) (value != nil && value != Nil && value != NULL && value != (id)[NSNull null])
and I invoke it like
if (IS_NULL(CurrencyCode))
{
//insert ""
}else
{
//do necessary updates
}
OR you can also use the following method ,
-(BOOL) isNull: (NSString*)value{
if ([value isEqualToString:#"<null>"]){
return false;
}
return true;
}
and you can invoke it like,
if (isNull(currencyType))
{
//insert #""
}else{
//do necessary updates
}
Try this this is mine code i am using i hope it would be helpful!!
func checkDictionary(let dict:NSMutableDictionary)
{
let keys = Array(dict.allKeys)
for i in keys
{
let checkvalue = dict.valueForKey(i as! String)
if checkvalue! .isKindOfClass(NSNull)
{
dict.setObject("", forKey: i as! NSString)
}
else if checkvalue!.isKindOfClass(NSDictionary)
{
let dic = checkvalue as! NSDictionary
let dicts = dic.mutableCopy()
self.checkDictionary(dicts as! NSMutableDictionary)
dict.setObject(dicts, forKey: i as! NSString)
}
else if checkvalue! .isKindOfClass(NSArray)
{
let keys2 = checkvalue as! NSArray
let keys1 = keys2.mutableCopy() as! NSMutableArray
dict.setObject(keys1, forKey: i as! NSString)
for j in keys1
{
if j .isKindOfClass(NSNull)
{
keys1.replaceObjectAtIndex(keys1.indexOfObject(j), withObject:"")
}
if j.isKindOfClass(NSDictionary)
{
let dic = j as! NSDictionary
let dicts = dic.mutableCopy()
keys1.replaceObjectAtIndex(keys1.indexOfObject(j), withObject: dicts)
self .checkDictionary(dicts as! NSMutableDictionary)
}
}
}
}
}
In objective-C Try this!!
-(void)CheckDictionary:(NSMutableDictionary *)dic
{
NSArray *Arr = [dic allKeys];
for (int i = 0; i<Arr.count; i++)
{
if ([[dic valueForKey:[Arr objectAtIndex:i]] isKindOfClass:[NSNull class]])
{
[dic setObject:#"" forKey:[Arr objectAtIndex:i]];
}
else if ([[dic valueForKey:[Arr objectAtIndex:i]] isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *dict = [[dic valueForKey:[Arr objectAtIndex:i]] mutableCopy];
[dic setObject:dict forKey:[Arr objectAtIndex:i]];
[self CheckDictionary:dict];
}
else if ([[dic valueForKey:[Arr objectAtIndex:i]] isKindOfClass:[NSMutableArray class]])
{
NSMutableArray *Arr12 = [dic valueForKey:[Arr objectAtIndex:i]];
for (int j = 0; j<Arr12.count; j++)
{
if ([[Arr12 objectAtIndex:j] isKindOfClass:[NSDictionary class]])
{
NSDictionary *dict123 = [Arr12 objectAtIndex:j];
NSMutableDictionary *dict = [dict123 mutableCopy];
[Arr12 replaceObjectAtIndex:j withObject:dict];
[self CheckDictionary:dict];
}
}
}
}
}
And Just pass the whole dictionary when you call this method I hope it would be helpful!!
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options: NSJSONReadingMutableContainers error: nil];
if (dictionary)
{
NSMutableDictionary *dict = [dictionary mutableCopy];
[self CheckDictionary:dict];
dictionary = [NSDictionary dictionaryWithDictionary:dict];
}
This is how we do it
#interface NSMutableArray (JSON)
- (void)recursivelyRemoveNulls;
#end
#implementation NSMutableArray (JSON)
- (void)recursivelyRemoveNulls
{
[self enumerateObjectsUsingBlock:^(id value, NSUInteger __unused idx, BOOL __unused *nestedStop)
{
if ([value isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *modifiedValue = [NSMutableDictionary dictionaryWithDictionary:value];
[modifiedValue recursivelyRemoveNulls];
[self removeObject:value];
[self addObject:modifiedValue];
}
else if ([value isKindOfClass:[NSArray class]])
{
NSMutableArray *modifiedValue = [NSMutableArray arrayWithArray:value];
[modifiedValue recursivelyRemoveNulls];
[self removeObject:value];
[self addObject:modifiedValue];
}
}];
}
#end
#interface NSMutableDictionary (JSON)
- (void)recursivelyRemoveNulls;
#end
#implementation NSMutableDictionary (JSON)
- (void)recursivelyRemoveNulls
{
[self enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL __unused *stop)
{
if (value == [NSNull null] || value == nil)
{
[self removeObjectForKey:key];
}
else if ([value isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *modifiedValue = [NSMutableDictionary dictionaryWithDictionary:value];
[modifiedValue recursivelyRemoveNulls];
self[key] = modifiedValue;
}
else if ([value isKindOfClass:[NSArray class]])
{
NSMutableArray *modifiedValue = [NSMutableArray arrayWithArray:value];
[modifiedValue recursivelyRemoveNulls];
self[key] = modifiedValue;
}
}];
}
#end
The short code you can use here:-
NSString * newValue=[self isNotNull:[your Object here]] ? [your Object here] : #"Value that you want to replace";
- (BOOL)isNull:(NSObject *)object {
if (!object) return YES;
else if (object == [NSNull null]) return YES;
else if ([object isKindOfClass:[NSString class]]) {
return ([((NSString *)object)isEqualToString : #""]
|| [((NSString *)object)isEqualToString : #"null"]
|| [((NSString *)object)isEqualToString : #"<null>"]
|| [((NSString *)object)isEqualToString : #"(null)"]
);
}
return NO;
}
- (BOOL)isNotNull:(NSObject *)object {
return ![self isNull:object];
}
Here's the source code for the method that appears to be causing the leak.
- (void)search:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
NSArray* fields = [command argumentAtIndex:0];
NSDictionary* findOptions = [command argumentAtIndex:1 withDefault:[NSNull null]];
[self.commandDelegate runInBackground:^{
// from Apple: Important You must ensure that an instance of ABAddressBookRef is used by only one thread.
// which is why address book is created within the dispatch queue.
// more details here: http: //blog.byadrian.net/2012/05/05/ios-addressbook-framework-and-gcd/
CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
// it gets uglier, block within block.....
[abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
if (addrBook == NULL) {
// permission was denied or other error - return error
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? (int)errCode.errorCode:UNKNOWN_ERROR];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
return;
}
NSArray* foundRecords = nil;
// get the findOptions values
BOOL multiple = NO; // default is false
NSString* filter = nil;
NSArray* desiredFields = nil;
if (![findOptions isKindOfClass:[NSNull class]]) {
id value = nil;
filter = (NSString*)[findOptions objectForKey:#"filter"];
value = [findOptions objectForKey:#"multiple"];
if ([value isKindOfClass:[NSNumber class]]) {
// multiple is a boolean that will come through as an NSNumber
multiple = [(NSNumber*)value boolValue];
// NSLog(#"multiple is: %d", multiple);
}
desiredFields = [findOptions objectForKey:#"desiredFields"];
// return all fields if desired fields are not explicitly defined
if (desiredFields == nil || desiredFields.count == 0) {
desiredFields = [NSArray arrayWithObjects:#"*", nil];
}
}
NSDictionary* searchFields = [[CDVContact class] calcReturnFields:fields];
NSDictionary* returnFields = [[CDVContact class] calcReturnFields:desiredFields];
NSMutableArray* matches = nil;
if (!filter || [filter isEqualToString:#""]) {
// get all records
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
if (foundRecords && ([foundRecords count] > 0)) {
// create Contacts and put into matches array
// doesn't make sense to ask for all records when multiple == NO but better check
int xferCount = multiple == YES ? (int)[foundRecords count] : 1;
matches = [NSMutableArray arrayWithCapacity:xferCount];
for (int k = 0; k < xferCount; k++) {
CDVContact* xferContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:k]];
[matches addObject:xferContact];
xferContact = nil;
}
}
} else {
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
matches = [NSMutableArray arrayWithCapacity:1];
BOOL bFound = NO;
int testCount = (int)[foundRecords count];
for (int j = 0; j < testCount; j++) {
CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]];
if (testContact) {
bFound = [testContact foundValue:filter inFields:searchFields];
if (bFound) {
[matches addObject:testContact];
}
testContact = nil;
}
}
}
NSMutableArray* returnContacts = [NSMutableArray arrayWithCapacity:1];
if ((matches != nil) && ([matches count] > 0)) {
// convert to JS Contacts format and return in callback
// - returnFields determines what properties to return
#autoreleasepool {
int count = multiple == YES ? (int)[matches count] : 1;
for (int i = 0; i < count; i++) {
CDVContact* newContact = [matches objectAtIndex:i];
NSDictionary* aContact = [newContact toDictionary:returnFields];
[returnContacts addObject:aContact];
}
}
}
// return found contacts (array is empty if no contacts found)
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:returnContacts];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
// NSLog(#"findCallback string: %#", jsString);
if (addrBook) {
CFRelease(addrBook);
}
}];
}]; // end of workQueue block
return;
}
The specific line that is doing most of the leaking is foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);, but this is confusing, given that the correct __bridge_transfer call is used. What's going on here?
this question is based on performance, i am getting desired results.
i have a array of dictionaries like this:
Printing description of arrAppointmentDictionary:
<__NSArrayM 0x16f962a0>(
{
"component_id" = 159;
total = 1;
},
{
"component_id" = 165;
total = 1;
},
{
"component_id" = 177;
total = 1;
},
{
"component_id" = 191;
total = 1;
},
{
"component_id" = 193;
total = 1;
}
)
i searched in dictionary based on keys like this:
for (int i = 0; i<arrAppointmentDictionary.count; i++)
{
NSMutableDictionary *appointmentDictionary = [arrAppointmentDictionary objectAtIndex:i];
NSArray *keys = [appointmentDictionary allKeys];
for (NSString *key in keys)
{
#autoreleasepool {
NSLog(#"Key is %#", key);
if([[appointmentDictionary objectForKey: key] isEqualToString:[rs stringForColumn:#"id"]])
{
layout.numberOfAppointments = [appointmentDictionary objectForKey: #"total"];
NSLog(#"number of appointments are >>>>>>>>>>>>>>>>> %#", [appointmentDictionary objectForKey: #"total"]);
}
}
}
}
i get the results accurate.
how to increase performance/memory optimisations of this for loop as it is called from another while loop.
thanks & regards.
Here's one way:
NSString *wantedId = [rs stringForColumn:#"id"];
for (NSMutableDictionary *appointmentDictionary in arrAppointmentDictionary) {
if ([appointmentDictionary[#"id"] isEqualToString:wantedId]) {
layout.numberOfAppointments = appointmentDictionary[#"total"];
NSLog(#"number of appointments are >>>>>>>>>>>>>>>>> %#", appointmentDictionary[#"total"]);
break;
}
}
I have a NSArray which looks like this:
#[#"file1", #"directory/testfile", #"directory/otherdir/testfile", #"otherdir/", #"otherdir/otherfile"]
And I'm trying to convert it to a NSDictionary which looks like this:
#{
#"directory/":#{
#"otherdir/":#{
#"testfile":[..some array with data..]}
},
#"testfile":[..some array with data..]
},
#"otherdir/":#{
#"otherfile":[..some array with data..]
},
#"file1":[..some array with data..]
}
Basically, a NSDictionary which looks like a file tree. Thanks in advance!
EDIT: The actual array I have is this:
(
"blog/",
"blog/code.css",
"blog/style.css",
"etc/",
"etc/awsservices.png",
"etc/speeds.png",
"etc/test/",
"etc/test/other/",
"etc/test/other/speeds.png",
"site/",
"site/dino.png",
"site/error.css",
"site/main.css",
"site/signet.min.js"
)
And the code I wrote for this:
-(NSDictionary*)buildDictionaryWithContentsOfPath:(NSMutableArray*)files {
NSString *subDir;
NSMutableDictionary *returnable = [[NSMutableDictionary alloc] initWithDictionary:#{}];
BOOL directory;
NSLog(#"%#", files);
for (S3ObjectSummary *objectSummary in files) {
if ([[objectSummary key] hasSuffix:#"/"]) {
if (subDir && [[objectSummary key] hasPrefix:subDir]) {
NSLog(#"prefixed %#", [objectSummary key]);
NSMutableDictionary *treeDict = [[NSMutableDictionary alloc] initWithDictionary:#{}];
[returnable[subDir] setObject:treeDict forKey:[objectSummary key]];
} else {
subDir = [objectSummary key];
NSLog(#"dir %#", [objectSummary key]);
NSMutableDictionary *treeDict = [[NSMutableDictionary alloc] initWithDictionary:#{}];
[returnable setObject:treeDict forKey:[objectSummary key]];
}
directory = true;
} else {
S3GetObjectMetadataRequest *getMetadataRequest = [[S3GetObjectMetadataRequest alloc] initWithKey:[objectSummary key] withBucket:self.bucket.name];
S3GetObjectMetadataResponse *getMetadataResponse = [self.client getObjectMetadata:getMetadataRequest];
if (![[objectSummary key] hasPrefix:subDir]) {
NSLog(#"file %#",[objectSummary key]);
[returnable setObject:#{#"filename":[objectSummary key], #"directory":#(directory), #"created_at":getMetadataResponse.lastModified} forKey:[objectSummary key]];
} else {
for (NSString __strong *pathComp in [[objectSummary key] pathComponents]) {
pathComp = [NSString stringWithFormat:#"%#/", pathComp];
NSLog(#"path component %#", pathComp);
if (![[objectSummary key] hasSuffix:#"/"]) {
if (returnable[pathComp]) {
NSLog(#"has comp %#", pathComp);
[returnable[pathComp] setObject:#{#"filename":[objectSummary key], #"directory":#(directory), #"created_at":getMetadataResponse.lastModified} forKey:[objectSummary key]];
}
}
}
}
directory = false;
}
//[self.fileTree addObject:#{#"filename":[objectSummary key], #"directory":#(directory), #"created_at":getMetadataResponse.lastModified}];
}
return returnable;
}
And the dictionary it returns:
{
"blog/" = {
"blog/code.css" = {
"created_at" = "2013-12-07 12:13:37 +0000";
directory = 0;
filename = "blog/code.css";
};
"blog/style.css" = {
"created_at" = "2013-12-07 12:13:36 +0000";
directory = 0;
filename = "blog/style.css";
};
};
"etc/" = {
"etc/awsservices.png" = {
"created_at" = "2013-12-07 12:26:37 +0000";
directory = 0;
filename = "etc/awsservices.png";
};
"etc/speeds.png" = {
"created_at" = "2013-12-07 13:29:27 +0000";
directory = 0;
filename = "etc/speeds.png";
};
// PROBLEM HERE
"etc/test/" = {
};
"etc/test/other/" = {
};
"etc/test/other/speeds.png" = {
"created_at" = "2013-12-07 17:29:21 +0000";
directory = 0;
filename = "etc/test/other/speeds.png";
};
// PROBLEM HERE
};
"site/" = {
"site/dino.png" = {
"created_at" = "2013-12-07 12:13:59 +0000";
directory = 0;
filename = "pmerino/dino.png";
};
"site/error.css" = {
"created_at" = "2013-12-07 12:13:59 +0000";
directory = 0;
filename = "pmerino/error.css";
};
"site/main.css" = {
"created_at" = "2013-12-07 12:13:59 +0000";
directory = 0;
filename = "pmerino/main.css";
};
"site/signet.min.js" = {
"created_at" = "2013-12-07 12:13:59 +0000";
directory = 0;
filename = "pmerino/signet.min.js";
};
};
}
As you can see, this part:
"etc/test/" = {
};
"etc/test/other/" = {
};
"etc/test/other/speeds.png" = {
"created_at" = "2013-12-07 17:29:21 +0000";
directory = 0;
filename = "etc/test/other/speeds.png";
};
Should look like this:
"etc/" = {
"test/" = {
"other/" = {
"speeds.png" = {
"created_at" = "2013-12-07 17:29:21 +0000";
directory = 0;
filename = "etc/test/other/speeds.png";
};
};
};
};
If I understand your problem correctly, this method should be helpful:
// Add the path to the tree. Returns the "node" for this path.
-(NSMutableDictionary *)addFile:(NSString *)path toTree:(NSMutableDictionary *)tree
{
NSMutableDictionary *node = tree;
// Split path into components:
NSArray *components = [path componentsSeparatedByString:#"/"];
// Create all intermediate dictionaries:
for (NSUInteger i = 0; i < [components count] - 1; i++) {
NSString *key = [components[i] stringByAppendingString:#"/"];
if (node[key] == nil) {
node[key] = [NSMutableDictionary dictionary];
}
node = node[key];
}
// Process last component, which is the file name,
// or "" in the case of a dictionary:
NSString *name = [components lastObject];
if ([name length] > 0) {
node[name] = [NSMutableDictionary dictionary];
node = node[name];
}
return node;
}
If you use it like this:
NSArray *files = ...; // Your array of files
NSMutableDictionary *tree = [NSMutableDictionary dictionary];
for (NSString *path in files) {
NSMutableDictionary *node = [self addFile:path toTree:tree];
BOOL isDir = [path hasSuffix:#"/"];
if (!isDir) {
node[#"filename"] = path;
}
}
the resulting dictionary is
{
"blog/" = {
"code.css" = {
filename = "blog/code.css";
};
"style.css" = {
filename = "blog/style.css";
};
};
"etc/" = {
"awsservices.png" = {
filename = "etc/awsservices.png";
};
"speeds.png" = {
filename = "etc/speeds.png";
};
"test/" = {
"other/" = {
"speeds.png" = {
filename = "etc/test/other/speeds.png";
};
};
};
};
"site/" = {
"dino.png" = {
filename = "site/dino.png";
};
"error.css" = {
filename = "site/error.css";
};
"main.css" = {
filename = "site/main.css";
};
"signet.min.js" = {
filename = "site/signet.min.js";
};
};
}