insertObject: atIndex: - index 3 beyond bounds for empty array - objective-c

I create an array based on a dictionaries key's:
factsBuiltArray = [NSMutableArray arrayWithCapacity: 6];
if ([statusDict count] == 10) {
for (NSString *key in [statusDict allKeys]) {
if ([key isEqualToString: #"currenciesAndConversions"]) {
[factsBuiltArray insertObject:key atIndex: 0];
}
else if ([key isEqualToString: #"languageAndTranslations"]) {
[factsBuiltArray insertObject:key atIndex: 1];
}
else if ([key isEqualToString: #"plugSize"]) {
[factsBuiltArray insertObject:key atIndex: 2];
}
else if ([key isEqualToString: #"timezone"]) {
[factsBuiltArray insertObject:key atIndex: 3]; // crashes over here
}
else if ([key isEqualToString: #"population"]) {
[factsBuiltArray insertObject:key atIndex: 4];
}
else if ([key isEqualToString: #"wikipedia"]) {
[factsBuiltArray insertObject:key atIndex: 5];
}
}
}
The crash log is:
*** -[__NSArrayM insertObject:atIndex:]: index 3 beyond bounds for empty array
Why does inserting an object to an array that is specified with a capacity of 6 make it crash? Very confusing!

The capacity is merely how many objects a container class can hold. Inserting at an index requires that index to be a valid placement for the new object based on the total number of objects contained in the container (not the total number of objects that CAN be contained).
If your array's values are index dependent (which is seems like perhaps a different architecture or data structure would be better) then you can ensure that every index is filled by prepopulating the array with NSNulls. This would require you to check for NSNulls when reading from the array later on though which would likely be extra work, hence why this is probably not the best approach. In any case, you can change your code to the following to fix your crash.
factsBuiltArray = [NSMutableArray arrayWithCapacity: 6];
for (NSUInter i = 0; i < 6; i++) {
[factsBuiltArray addObject:[NSNull null]];
}
if ([statusDict count] == 10) {
for (NSString *key in [statusDict allKeys]) {
if ([key isEqualToString: #"currenciesAndConversions"]) {
[factsBuiltArray replaceObjectAtIndex:0 withObject:key];
}
else if ([key isEqualToString: #"languageAndTranslations"]) {
[factsBuiltArray replaceObjectAtIndex:1 withObject:key];
}
else if ([key isEqualToString: #"plugSize"]) {
[factsBuiltArray replaceObjectAtIndex:2 withObject:key];
}
else if ([key isEqualToString: #"timezone"]) {
[factsBuiltArray replaceObjectAtIndex:3 withObject:key];
}
else if ([key isEqualToString: #"population"]) {
[factsBuiltArray replaceObjectAtIndex:4 withObject:key];
}
else if ([key isEqualToString: #"wikipedia"]) {
[factsBuiltArray replaceObjectAtIndex:5 withObject:key];
}
}
}

Related

CS193P assignment 2 Clear function

Given this implementation:
- (NSMutableArray *)programStack
{
if (_programStack == nil)
_programStack = [[NSMutableArray alloc]init];
return _programStack;
}
- (id)program
{
return [self.programStack copy];
}
+ (double)popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack)
[stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]]) {
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]])
{
NSString *operation = topOfStack;
// C
if ([operation isEqualToString:#"C"])
{
[stack removeAllObjects];
return 0;
}
}
}
Am I correct in assuming that the class method's call to [stack removeAllObjects] only affects a copy of a copy rather than removing all objects from the instance's _programStack ? How would you, from that class method, affect the instance's variable? Or is there a better way to do this?
Thanks.
[stack removeAllObjects]; will remove all objects from stack. If you call + (double)popOperandOffStack:(NSMutableArray *)stack from an object, passing an instance variable as stack, then popOperandOffStack: operates on that instance variable and will remove all objects:
[[self class] popOperandOffStack:self.myInstanceArray]
If, on the other hand, you call [[self class] popOperandOffStack:[self.myInstanceArray mutableCopy]] it will operate on a copy.

Reusable method to check if all objects are unequal using valueForKey

I have been working with this method for hours. It is supposed to simply check if the attribute of the cards passed in as otherCards isEqual to the same attribute of this (self) instance of class. But I am tearing my hair out - it is giving false results
(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSArray *values = [NSArray arrayWithArray:[otherCards valueForKey:key]];
int countUnequalMatches = 0;
for (NSNumber *setValue in values) {
if (![[self valueForKey:key] isEqual:setValue]) {
countUnequalMatches++;}
}
NSLog(#"countUnequalMatches %d values count: %d", countUnequalMatches, [values count]);
if (countUnequalMatches == [values count]) return YES ?: NO;
}
It is called like this:
if ([self otherCards:otherCards allHaveUnequalAttribute:CARD_SHAPE]) {
NSLog(#"All unequal");
} else {
NSLog(#"One or more card equal");
}
I found the bug myself
if (countUnequalMatches == [values count]) return YES ?: NO;
should be:
if (countUnequalMatches == [otherCards count]) return YES ?: NO;
as I used the Set as an way to count unique items.
This is the sort of thing that predicates are very well suited to handle.
-(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K == %#", key, [self valueForKey:key]];
NSArray *equalCards = [otherCards filteredArrayUsingPredicate:pred];
return equalCards.count == 0;
}

NSDictionary in NSArray search

I'm having a problem with some search code. I have an NSMutableArray, called searchedData, that contains NSDictionaries (one per object). Here's the search code I have now:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects]; // remove all data that belongs to previous search
if([searchText isEqualToString:#""]||searchText==nil) {
[tableView reloadData];
return;
}
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if (titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
[tableView reloadData];
}
It finds the number of entries, then goes to each entry, finds the objects for keys "TITLE" and "AUTHOR", and then displays entries in the UITableView if there is a match.
The problem is that it never displays anything, even if there is a match.
I know the table / dictionaries are not null (I have NSLogged it) so I don't know why it's not working.
You forgot to reload the tableView having added the relevant data to the datasource.
I have improved the coding of the method a bit, and added the missing line.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if (![searchText isEqualToString:#""] || searchText != nil) {
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if(titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
}
[tableView reloadData];
}

Replace all NSNull objects in an NSDictionary

I'm curious, I currently have an NSDictionary where some values are set to an NSNull object thanks to the help of json-framework.
The aim is to strip all NSNull values and replace it with an empty string.
I'm sure someone has done this somewhere? No doubt it is probably a four liner and is simple, I am just far too burnt out to figure this out on my own.
I've made a few changes to Jacob's original answer to extend it to handle dictionaries and arrays stored within the original dictionary.
#import "NSDictionary+NullReplacement.h"
#import "NSArray+NullReplacement.h"
#implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
#end
And there's also an array category of course:
#import "NSArray+NullReplacement.h"
#import "NSDictionary+NullReplacement.h"
#implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
#end
With this, you can take any array or dictionary and recursively wipe out all the [NSNull null] instances.
P.S. For completion's sake, here are the header files:
#interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
#end
And the array header:
#interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
#end
Really simple:
#interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
#end
#implementation NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
#end
Usage:
NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Rolling through the dictionary hunting for NSNull is one way to tackle the problem, but I took a slightly lazier approach. Instead of nil you could assign an empty string, but the principle is the same.
CPJSONDictionary.h
#interface NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey;
#end
CPJSONDictionary.m
#implementation NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey {
id object = [self objectForKey:aKey];
if ([object isKindOfClass:[NSNull class]]) {
object = nil;
}
return object;
}
#end
I have tested Stakenborg solution. It works well, but it has following problem. If some object is expected to be number, for instance, converting it to NSNull can be a source of error.
I have create a new method to directly remove the NSNull entries. This way you only have to check that correspondant key exists.
Add in NSDictionary+NullReplacement
- (NSDictionary *)dictionaryByRemovingNulls{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced removeObjectForKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByRemovingNulls] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByRemovingNulls] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
And in NSArray+NullReplacement
- (NSArray *)arrayByRemovingNulls {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (int idx = [replaced count]-1; idx >=0; idx--) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced removeObjectAtIndex:idx];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByRemovingNulls]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByRemovingNulls]];
}
return [replaced copy];
}
another variation:
NSDictionary * NewDictionaryReplacingNSNullWithEmptyNSString(NSDictionary * dict) {
NSMutableDictionary * const m = [dict mutableCopy];
NSString * const empty = #"";
id const nul = [NSNull null];
NSArray * const keys = [m allKeys];
for (NSUInteger idx = 0, count = [keys count]; idx < count; ++idx) {
id const key = [keys objectAtIndex:idx];
id const obj = [m objectForKey:key];
if (nul == obj) {
[m setObject:empty forKey:key];
}
}
NSDictionary * result = [m copy];
[m release];
return result;
}
The result is the same as, and it appears pretty much identical to Jacob's, but the speed and memory requirements are one half to one third (ARC or MRC) in the tests I made. Of course, you could also use it as a category method as well.
Here is my solution:
+ (NSDictionary *)cleanNullInJsonDic:(NSDictionary *)dic
{
if (!dic || (id)dic == [NSNull null])
{
return dic;
}
NSMutableDictionary *mulDic = [[NSMutableDictionary alloc] init];
for (NSString *key in [dic allKeys])
{
NSObject *obj = dic[key];
if (!obj || obj == [NSNull null])
{
// [mulDic setObject:[#"" JSONValue] forKey:key];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
[mulDic setObject:[self cleanNullInJsonDic:(NSDictionary *)obj] forKey:key];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *array = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulDic setObject:array forKey:key];
}else
{
[mulDic setObject:obj forKey:key];
}
}
return mulDic;
}
+ (NSArray *)cleanNullInJsonArray:(NSArray *)array
{
if (!array || (id)array == [NSNull null])
{
return array;
}
NSMutableArray *mulArray = [[NSMutableArray alloc] init];
for (NSObject *obj in array)
{
if (!obj || obj == [NSNull null])
{
// [mulArray addObject:[#"" JSONValue]];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary *dic = [self cleanNullInJsonDic:(NSDictionary *)obj];
[mulArray addObject:dic];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *a = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulArray addObject:a];
}else
{
[mulArray addObject:obj];
}
}
return mulArray;
}
-(NSDictionary*)stripNulls:(NSDictionary*)dict{
NSMutableDictionary *returnDict = [NSMutableDictionary new];
NSArray *allKeys = [dict allKeys];
NSArray *allValues = [dict allValues];
for (int i=0; i<[allValues count]; i++) {
if([allValues objectAtIndex:i] == (NSString*)[NSNull null]){
[returnDict setValue:#"" forKey:[allKeys objectAtIndex:i]];
}
else
[returnDict setValue:[allValues objectAtIndex:i] forKey:[allKeys objectAtIndex:i]];
}
return returnDict;
}
A category on nsnull that returns nil seems to also sense, at least to me. There are a few out there. One makes all calls return nil which seems to make sense. Sorry no link. I guess if you need to later use nspropertylistserialization the category might not work for you.

Is there a better way to implement this?

I have an object that has properties for a person's address. For convenience, I have written a method to generate an NSString with the person's full address. My implementation is:
/**
Returns the full address in US format of the Addressable object.
*/
- (NSString *)fullAddress {
NSMutableString *ret = [NSMutableString string];
if (self.company) {
[ret appendFormat:#"%#\n", self.company];
}
if (self.firstName) {
[ret appendFormat:#"%#", self.firstName];
}
if (self.firstName && self.lastName) {
[ret appendString:#" "];
}
if (self.lastName) {
[ret appendFormat:#"%#", self.firstName];
}
if (self.firstName || self.lastName) {
[ret appendString:#"\n"];
}
if (self.address) {
[ret appendFormat:#"%#\n", self.address];
}
if (self.addressLine2 && ![self.addressLine2 isEqualToString:#""]) {
[ret appendFormat:#"%#\n", self.addressLine2];
}
if (self.addressLine3 && ![self.addressLine3 isEqualToString:#""]) {
[ret appendFormat:#"%#\n", self.addressLine3];
}
if (self.city) {
[ret appendString:self.city];
}
if (self.city && self.state) {
[ret appendString:#", "];
}
if (self.state) {
[ret appendString:self.state];
}
if (self.zip) {
[ret appendFormat:#" %#", self.zip];
}
return ret;
}
This feels clumsy to me. Is there a better way to do this?
I don't think so.
You could loop through the properties, but since you're not appending a consistent string, you're not going to save yourself any trouble by doing so.