ObjectForKey is returning nil sometimes - objective-c

I am trying to set an object to a dictionary with key as an object. The test cases works fine, but within the actual code, I am not able to get the value for the key. The NSMutableDictionary has the key value in it, but when debugging it returns nil.
#implementation JSHashMap {
NSMutableDictionary *dict;
}
- (instancetype)initWithArray:(NSMutableArray *)array {
self = [super init];
if (self) {
dict = [self fromArray:array];
}
return self;
}
- (NSMutableDictionary *)fromArray:(NSMutableArray *)array {
NSMutableDictionary* _dict = [NSMutableDictionary new];
NSUInteger i = 0, len = [array count];
if (len % 2 != 0) {
error(#"JSError: Odd number of elements in the array.");
return _dict;
}
for (i = 0; i < len; i = i + 2) {
[_dict setObject:array[i + 1] forKey:array[i]];
assert([_dict objectForKey:array[i]] != nil);
}
debug(#"%#", _dict);
return _dict;
}
- (JSData *)objectForKey:(id)key {
return [dict objectForKey:key];
}
I am creating the hash map using the initWithArray method.
(lldb) po [dict objectForKey:key]
nil
The key passed in and the key in the dictionary has the same memory address 0x100ea2fa0.
The test cases works fine though. But the when running the actual program, it fails.
NSMutableDictionary *dict = [NSMutableDictionary new];
JSNumber *val = [[JSNumber alloc] initWithInt:1];
JSNumber *key = [[JSNumber alloc] initWithInt:2];
[dict setObject:val forKey:key];
JSData * ret = [dict objectForKey:key];
XCTAssertNotNil(ret);
XCTAssertEqualObjects([ret dataType], #"JSNumber");
JSHashMap *hm = [[JSHashMap alloc] initWithArray:[#[key, val] mutableCopy]];
JSData * ret1 = [hm objectForKey:key];
XCTAssertNotNil(ret1);
XCTAssertEqualObjects([ret1 dataType], #"JSNumber");
JSHashMap *dict = (JSHashMap *)ast;
NSArray *keys = [dict allKeys];
NSUInteger i = 0;
NSUInteger len = [keys count];
for (i = 0; i < len; i++) {
id key = keys[i];
JSData *val = (JSData *)[dict objectForKey:key];
// Issue -> val is getting nil
}
How to fix this and why is this random behaviour?
Found the failing test case.
NSArray *keys = [hm allKeys];
XCTAssertTrue([keys count] == 1);
JSData *ret = [hm objectForKey:keys[0]];
XCTAssertNotNil(ret);
If I use the key returned from calling allKeys method, it returns nil.

You have not shown any information about what JSNumber is, but I am betting that it does not implement isEqual and hash correctly. Thus, you cannot successfully use it as a key in an NSDictionary.

Related

Find a dictionary in ArrayOfDictionaries with a specific keyvalue pair

I have an array of dictionaries
for(int i = 0; i < 5; i++) {
NSMutableDictionary *_myDictionary = [NSMutableDictionary dictionary];
[_myDictionary setObject:[NSString stringWithFormat:#"%d",i] forKey:#"id"];
[_myDictionary setObject:label.text #"Name"];
[_myDictionary setObject:label1.text #"Contact"];
[_myDictionary setObject:label2.text #"Gender"];
}
[_myArray addObject:_myDictionary];
Now I want to pick a dictionary from the array whose objectForKey:#"id" is 1 or 2 or something else like there is an sql query Select * from Table where id = 2.
I know this process
int index = [_myArray count];
for(int i = 0; i < 5; i++)
{
NSMutableDictionary *_myDictionary = [NSMutableDictionary dictionaryWithDictionary:[_myArray objectAtIndex:i]];
if([[_myDictionary objectForKey:#"id"] isEqualToString:id])
{
index = i;
return;
}
}
if(index != [_myArray count])
NSLog(#"index found - %i",index);
else
NSLog(#"index not found");
Any help would be appreciated.
Thanks in Advance !!!
Try this, this is by using fast enumeration
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for(dict in _myArray)
{
if([[dict valueForKey:#"id"] isEqualToString:#"1"])
{
return;
}
}
You should use [NSNumber numberWithInteger: i] not a NSString.
Code fore this search should look like this:
NSString *valueToFind = [NSString stringWithFormat:#"%d", intValue]; // [NSNumber numberWithInteger: intValue]
NSInteger index = [_myArray indexOfObjectPassingTest:^BOOL(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
return [valueToFind isEqualToString: [obj objectForKey: #"id"]];
}];
NSDitionary *found = [_myArray objectAtIndex: index];

Adding objects from array to dictionary

I am getting rows from a SQLite DB and trying to insert them into a dictionary. Except I keep getting errors! I get the error "Implicit conversion of an Objective-C pointer to 'const id *' is disallowed with ARC" Which I know means that I cant use a pointer when I am adding objects to my dictionary. So how do I go about fixing it so I can add those arrays to a dictionary?
NSArray *keyArray = [[NSMutableArray alloc] init ];
NSArray *valueArray = [[NSMutableArray alloc ] init ];
NSDictionary* dic;
NSInteger dataCount = sqlite3_data_count(statement);
while (sqlite3_step(statement) == SQLITE_ROW) {
#try {
for (int i = 0; i < dataCount; i ++)
{
NSString* key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
NSString *value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
if ([value length] == 0)
{
value = #"";
}
keyArray = [keyArray arrayByAddingObject:key];
valueArray = [valueArray arrayByAddingObject:value];
}
}
#catch (NSException *ex)
{
NSLog(#"%#,%#", [ex name], [ex reason]);
}
dic= [NSDictionary dictionaryWithObjects:valueArray forKeys:keyArray count:[keyArray count]];
The dictionaryWithObjects:forKeys:count: takes C-style arrays, not NSArray objects. The dictionaryWithObjects:forKeys: may do the trick, but you may be better off constructing a mutable dictionary as you go, bypassing NSArrays entirely.
NSDictionary* dic;
NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
for (int i = 0; i < dataCount; i ++)
{
NSString* key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
NSString *value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, i)];
[tmp setObject:value forKey:key];
}
dict = tmp;
[dicBodyPost setValue:arrContactAddress forKey:#"contactAddress"];

How to exclude previously randomly selected NSArrays in loop

I'm new to Objective-C and I'm trying to create a simple dictionary style app for personal use. Right now I'm attempting to make a loop that prints randomly selected NSArrays that have been added to an NSDictionary. I'd like to print each array only once. Here is the code I'm working with:
NSArray *catList = [NSArray arrayWithObjects:#"Lion", #"Snow Leopard", #"Cheetah", nil];
NSArray *dogList = [NSArray arrayWithObjects:#"Dachshund", #"Pitt Bull", #"Pug", nil];
...
NSMutableDictionary *wordDictionary = [[NSMutableDictionary alloc] init];
[wordDictionary setObject: catList forKey:#"Cats"];
[wordDictionary setObject: dogList forKey:#"Dogs"];
...
NSInteger keyCount = [[wordDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
int i = keyCount;
for (i=i; i>0; i--) {
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
NSLog(#"%#", randomlySelectedArray);
}
This code prints the same array "i" times. Any pointers on how to exclude previously printed arrays from being printed again?
I'm wondering if removeObjectForKey: could be of any use.
You just need to re-calculate the random key index every time you go through the loop, and then, as you suggest, use removeObjectForKey:.
Something like this:
NSArray *catList = [NSArray arrayWithObjects:#"Lion", #"Snow Leopard", #"Cheetah", nil];
NSArray *dogList = [NSArray arrayWithObjects:#"Dachshund", #"Pitt Bull", #"Pug", nil];
//...
NSMutableDictionary *wordDictionary = [[NSMutableDictionary alloc] init];
[wordDictionary setObject: catList forKey:#"Cats"];
[wordDictionary setObject: dogList forKey:#"Dogs"];
//...
while ([wordDictionary count] > 0) {
NSInteger keyCount = [wordDictionary count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
NSLog(#"%#", randomlySelectedArray);
[wordDictionary removeObjectForKey: randomKey];
}
In your code, you generate a random randomKeyIndex, then use it without changing its value i times in the loop. So you get i times the same array.
NSInteger randomKeyIndex = arc4random() % keyCount;
// ...
for (i=i; i>0; i--) {
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
// ...
}
As you say removeObjectForKey is an option for you, you can change your code into something like this:
NSInteger keyCount = [[wordDictionary allKeys] count];
for (i=keyCount; i>0; i--) {
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
[wordDictionary removeObjectForKey:randomKey];
keyCount--;
NSLog(#"%#", randomlySelectedArray);
}

Extract a NSDictionary from a NSMutableArray

I need to extract a NSDictionary from a NSMutableArray, and extract an object from that dictionary.
The code should be quite easy, but I keep having a SIGABRT error on the NSDictionary declaration.
-(void)calcolaConto {
conto = [[NSNumber alloc] initWithDouble:0];
for (int i=0; [shoppingListItems count]; ++i) {
NSDictionary *dictVar = (NSDictionary *) [shoppingListItems objectAtIndex:i]; //<-- SIGABRT
NSNumber *IO = (NSNumber *) [dictVar objectForKey:#"incout"];
NSNumber *priceValue = (NSNumber *) [dictVar objectForKey:#"price"];
if ([IO isEqualToNumber:[NSNumber numberWithInt:0]]) {
conto = [NSNumber numberWithDouble:([conto doubleValue] + [priceValue doubleValue])];
} else if ([IO isEqualToNumber:[NSNumber numberWithInt:1]]) {
conto = [NSNumber numberWithDouble:([conto doubleValue] - [priceValue doubleValue])];
}
NSLog(#"Valore %#", conto);
}
}
"shoppingListItems" is created in this way:
NSMutableDictionary *rowDict = [[NSMutableDictionary alloc] initWithCapacity:6];
[rowDict setObject:primaryKeyValue forKey: ID];
[rowDict setObject:itemValue forKey: ITEM];
[rowDict setObject:priceValue forKey: PRICE];
[rowDict setObject:groupValue forKey: GROUP_ID];
[rowDict setObject:incOut forKey:INC_OUT];
[rowDict setObject:dateValue forKey: DATE_ADDED];
[shoppingListItems addObject: rowDict];
The problem is that your loop never stops. You should use:
for (NSUInteger i = 0; i < [shoppingListItems count]; i++) {
or:
for (NSDictionary* dictVar in shoppingListItems) {
so that you do not try to access an element that is out of bounds. In your
current loop i will be incremented until it reaches [shoppingListItems count]
which is beyond the end of the array, so objectAtIndex will throw an exception.

how to remove an NSMutableArray object by key?

i have structured an NSMutableArray and here is an example
(
{
Account = A;
Type = Electricity;
},
{
Account = B;
Type = Water;
},
{
Account = C;
Type = Mobile;
} )
when i try to delete Account B using
[data removeObject:#"B"];
Nothing Happens
[[NSUserDefaults standardUserDefaults] synchronize];
NSArray *archivedArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
if (archivedArray == nil) {
data = [[NSMutableArray alloc] init];
} else {
data = [[NSMutableArray alloc] initWithArray:archivedArray];
}
If you're actually using an array and not a dictionary, you need to search for the item before you can remove it:
NSUInteger index = [data indexOfObjectPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop) {
return [[(NSDictionary *)obj objectForKey:#"Account"] isEqualToString:#"B"];
}];
if (index != NSNotFound) {
[data removeObjectAtIndex:index];
}
Alternative: try a NSMutableDictionary:
NSArray *accounts = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
NSArray *types = [NSArray arrayWithObjects:#"Electricity", #"Water", #"Mobile", nil];
NSMutableDictionary* data = [NSMutableDictionary dictionaryWithObjects:types forKeys:accounts];
[data removeObjectForKey:#"B"];
An NSArray is like a list of pointers, each pointer points to an object.
If you call:
[someArray removeObject:#"B"];
You create a new NSString object that contains the string "B". The address to this object is different from the NSString object in the array. Therefore NSArray cannot find it.
You will need to loop through the array and determine where the object is located, then you simply remove it by using removeObjectAtIndex: