NSArray recursive search with index - ios

I try to find item in array by index
http://content.screencast.com/users/xdozorx/folders/Jing/media/cb5e24cf-9349-4aa2-a886-bfafb96299f5/00000051.png
here my code.
- (NSDictionary *) getItemAtIntex:(int) index inArray:(NSArray *) array
{
for (NSDictionary *item in array)
{
if (enumerateIndex == index)
{
NSLog(#"%#",item[#"comment"]);
return item;
}
else if ([item[#"childs"] count])
{
enumerateIndex ++;
[self getItemAtIntex:index inArray:item[#"childs"]];
}
}
return nil;
}
I call my method
enumerateIndex = 0;
NSDictionary *comment = [self getItemAtIntex:indexPath.row inArray:comments];
for example, with index 1, in debug I have two answers
subGL 1 -> 1
global 2
I need in response only subGL 1 -> 1
here my file https://www.dropbox.com/s/mvc21a8pl5n6xz3/commenst.json

You aren't doing anything with the return value of the recursive call. Try this:
- (NSDictionary *) getItemAtIntex:(int) index inArray:(NSArray *) array
{
for (NSDictionary *item in array)
{
if (enumerateIndex == index)
{
NSLog(#"%#",item[#"comment"]);
return item;
}
else if ([item[#"childs"] count])
{
enumerateIndex ++;
NSDictionary *result = [self getItemAtIntex:index inArray:item[#"childs"]];
if (result) {
return result;
}
}
}
return nil;
}

Related

Having troubles with UIPickerView

So I am getting a bizarre error when using UIPickerView. Before jumping into the code, a basic explanation of the app is in order:
This app uses a UISegmentedControl to select from a few different possibilities. Based on the selection from the segmented control, the UIPickerView is populated with different arrays. The picker view is guaranteed to have two (no more, no less) components. The arrays are of different sizes, this is where I run into the snag. I have some tests set up in the code to keep the values within the range of the arrays, but something is not working right.
Now onto the code:
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSMutableArray *array;
int x = self.segmentController.selectedSegmentIndex;
if (x == 0) {
array = [NSMutableArray arrayWithArray:self.length];
}
else if (x == 1) {
array = [NSMutableArray arrayWithArray:self.time];
}
else if (x == 2) {
array = [NSMutableArray arrayWithArray:self.speed];
}
//component == 0 has no problems whatsoever
if (component == 0) {
if (row < [array count]){
self.label1.text = [array objectAtIndex:row];
self.component0 = row;
}
else {
self.label1.text = [array objectAtIndex:[array count] - 1];
self.component0 = (NSInteger)([array count] - 1);
}
}
//component == 1 has problems, it always thinks that the row number is less than the array count
else if (component == 1) {
if (row < [array count]){
self.label2.text = [array objectAtIndex:row];
self.secondComponent = row;
}
else {
self.label2.text = [array objectAtIndex:[array count] - 1];
self.secondComponent = (NSInteger)([array count] - 1);
}
}
else {
NSLog(#"ERROR: Components out of range");
}
//If I comment out this part, the program works as expected
if (x == 0) {
[self convertLength];
}
else if (x == 1) {
[self convertTime];
}
else if (x == 2) {
[self convertSpeed];
}
}
The error comes when trying to read a value from an array, this is the error:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 8 beyond bounds [0 .. 7]'
I get this error when I go the the second segment (time) and scroll all the way to the bottom on the second component, and then go to the first index.
If I try to do the same thing with the first component, it works exactly the way it is supposed to, hence I am very confused, as I use basically the exact same code to control the two components.
This is how I load/reload the picker view:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
int x = 0;
if (self.segmentController.selectedSegmentIndex == 0) {
x = [self.length count];
}
else if (self.segmentController.selectedSegmentIndex == 1)
{
x = [self.time count];
}
else if (self.segmentController.selectedSegmentIndex == 2)
{
x = [self.speed count];
}
return x;
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *string;
NSArray *array = [[NSArray alloc] init];
if (self.segmentController.selectedSegmentIndex == 0) {
array = [NSArray arrayWithArray:self.length];
}
else if (self.segmentController.selectedSegmentIndex == 1)
{
array = [NSArray arrayWithArray:self.time];
}
else if (self.segmentController.selectedSegmentIndex == 2)
{
array = [NSArray arrayWithArray:self.speed];
}
if (row >= [array count]) {
string = [array objectAtIndex:[array count] - 1];
NSLog(#"Out of Range");
}
else {
string = [array objectAtIndex:row];
}
return string;
}
- (IBAction)segmentDidChange:(id)sender {
[self.pickerView reloadAllComponents];
[self pickerView:self.pickerView didSelectRow:self.component0 inComponent:0];
[self pickerView:self.pickerView didSelectRow:self.secondComponent inComponent:1];
}
Other useful information:
length array count: 8
time array count: 9
speed array count: 8
When I log the variables, they behave the way they are expected to, they change their value to be within the range of the "controller array" before I try to access any data within said array, but for whatever reason the second component wants to access an index not contained within the array.

Memory leak in ios cordova contacts plugin?

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?

Remove object from NSMutable array wont work

I have an NSMutable array which i'm trying to remove objects from and by that to decrease
the count of number of objects on the array, and the count always stays the same,
Meaning, the remove is not working.
Here is the code (it's from the online stanford IOS development course):
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (void)addCard:(Card *)card
{
[self addCard:card atTop:NO];
}
- (Card *)drawRandomCard
{
Card* randomCard = Nil;
NSLog(#"This is the count %d",[self.cards count]);
if ([self.cards count]) {
unsigned index = arc4random() % [self.cards count];
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
The count is always 52, even after removing the objects.
Any ideas on how to fix this?

json find parent node of node where key named "id" equal to "value"

I have JSON similar to this with
many id and ref keys:
jSon = {
"id":"node1",
"name":"Languages",
"rel":"",
"children":1,
"step":1,
"path":1,
"nodes":[
{
"id":"node2",
"name":"Java",
"rel":"Pure Object Oriented Prog",
"children":1,
"step":2,
"path":2,
"nodes":[
{
"id":"node3",
"name":"C#",
"rel":"Framework",
"children":0,
"step":3,
"path":3,
"nodes":[]
},{
"id":"node4",
"name":"C++",
"rel":"OOPS",
"children":0,
"step":3,
"path":4,
"nodes":[]
}]
}]
};
In Objective-C how can I reach to parent node of element where key is id and value is let's say node4 or something else.
In xcode, I am trying to parse a JSON data from MVC web API.
My web.api JSON is this
When I comment out Newtonsoft.Json.PreserveReferencesHandling.Objects, JSON becomes very big, usually like 35MB.
When I use PreserveReferencesHandling.Objects , .net puts id and ref keys for repating objects.
When traveling through JSON, if a reach a ref key with value asdfg, I have to search back node with key id and with value `asdf.
Is this possible in Xcode ?
You'd need to either do a depth-first or breadth-first search, depending on which you think will find the node fastest. Here's a very un-tested attempt at the code you'd need:
Depth-first
- (id)nodeWithID:(NSString *)identifier inDict:(NSDictionary *)dict
{
id node = nil;
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
// Traverse the inner nodes to perform a depth-first search
for (NSDictionary *innerNode in dict[#"nodes"])
{
node = [self nodeWithID:identifier inDict:innerNode];
// Break once a node is found
if (node != nil)
{
break;
}
}
}
return node;
}
Breadth-first
- (id)nodeWithID:(NSString *)identifier inDict:(NSDictionary *)dict
{
id node = nil;
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
// Perform a breadth-first search
NSMutableArray *nextDictionaries = [[NSMutableArray alloc] initWithArray:dict[#"nodes"]];
while (node == nil && [nextDictionaries count] > 0)
{
dict = nextDictionaries[0];
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
[nextDictionaries addObjectsFromArray:dict[#"nodes"]];
[nextDictionaries removeObjectAtIndex:0];
}
}
}
return node;
}
* EDIT *
Or if you want to be fancy and make a category on NSDictionary:
typedef NS_ENUM(NSUInteger, NSIdentifierSearchOptions) {
NSIdentifierSearchOptionBreadthFirst = 0,
NSIdentifierSearchOptionDepthFirst = 1
};
#interface NSDictionary (IdentifierSearch)
- (id)objectForKeyIdentifier:(NSString *)identifier options:(NSIdentifierSearchOptions)options;
#end
#implementation NSDictionary (IdentifierSearch)
- (id)objectForKeyIdentifier:(NSString *)identifier options:(NSIdentifierSearchOptions)options
{
id object = nil;
switch (options)
{
case NSIdentifierSearchOptionBreadthFirst:
{
object = [[self class] breadthFirstNodeWithID:identifier inDict:self];
break;
}
case NSIdentifierSearchOptionDepthFirst:
{
object = [[self class] depthFirstNodeWithID:identifier inDict:self];
break;
}
}
return object;
}
+ (id)breadthFirstNodeWithID:(NSString *)identifier inDict:(NSDictionary *)dict
{
id node = nil;
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
// Perform a breadth-first search
NSMutableArray *nextDictionaries = [[NSMutableArray alloc] initWithArray:dict[#"nodes"]];
while (node == nil && [nextDictionaries count] > 0)
{
dict = nextDictionaries[0];
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
[nextDictionaries addObjectsFromArray:dict[#"nodes"]];
[nextDictionaries removeObjectAtIndex:0];
}
}
}
return node;
}
+ (id)depthFirstNodeWithID:(NSString *)identifier inDict:(NSDictionary *)dict
{
id node = nil;
if ([dict[#"id"] isEqualToString:identifier])
{
node = dict;
}
else
{
// Traverse the inner nodes to perform a depth-first search
for (NSDictionary *innerNode in dict[#"nodes"])
{
node = [self depthFirstNodeWithID:identifier inDict:innerNode];
// Break once a node is found
if (node != nil)
{
break;
}
}
}
return node;
}
#end

Case-Insensitive array search

I am trying to search an NSMutableArray for a string. I am using containsObject: currently but it looks like this is case sensitive. I need to search for all combinations of the given string (trip). Any ideas would be greatly appreciated.
if ([self.theArray containsObject:trip]) {
}
Not that hard:
BOOL found = NO;
for (NSString* str in self.theArray) {
if ([str caseInsensitiveCompare:trip] == NSOrderedSame) {
found = YES;
break;
}
}
Create a category for NSArray and add this function in there.
- (BOOL)containsStringCaseInsensitive:(NSString *)key {
key = [key lowercaseString];
for (int i = ([self count] - 1); i >= 0; i--) {
NSObject * obj = [self objectAtIndex:i];
if ([obj isKindOfClass:[NSString class]]) {
NSString * strInArray = [(NSString *)obj lowercaseString];
if ([key isEqualToString:strInArray]) {
return YES;
}
}
}
return NO;
}
How about a block:
__block trip = #"blah";
if (NSNotFound!=[self.theArray indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop)
{
if (NSOrderedSame==[(NSString *)obj caseInsensitiveCompare:trip])
{
stop=YES;
return YES;
}
return NO;
}])
{
NSLog(#"It's a MATCH!");
}

Resources