here's a very basic question, that I'm sure you will be able to answer quickly. Please don't laugh at my ignorance.
I have a string, that I want to compare to an array of strings. Only if the string is not part of the array, I want to perform an operation. I tried the following code, that doesn't work. I do understand why, but I just can't think of a way to do it correctly.
Please help me out of my misery.
Thanks in advance
Sjakelien
-(void) findRedundant: (NSString *) aString {
#define ALPHA_ARRAY [NSArray arrayWithObjects: #"A", #"B", #"C", nil]
NSUInteger f;
for (f = 0; f < [ALPHA_ARRAY count]; f++)
{
NSString * stringFromArray = [ALPHA_ARRAY objectAtIndex:f];
if ([aString isEqualToString:stringFromArray]) {
// do nothing
} else {
//do something
}
}
}
[self findRedundant:#"D"];
Your code appears to work fine. Its terrible code, but it works fine, the // do nothing section is called for any match and the // do something section is called for each mismatch in the array. I suspect the problem is that you are expecting the // do nothing section to be executed once if there is no match, and // do something section to be executed once if there is any match, which is not the case. You probably want:
-(void) findRedundant: (NSString *) aString {
#define ALPHA_ARRAY [NSArray arrayWithObjects: #"A", #"B", #"C", nil]
BOOL found = NO;
NSUInteger f;
for (f = 0; f < [ALPHA_ARRAY count]; f++) {
NSString * stringFromArray = [ALPHA_ARRAY objectAtIndex:f];
if ([aString isEqualToString:stringFromArray]) {
found = YES;
break;
}
}
if ( found ) {
// do found
} else {
// do not found
}
}
Also, you clearly do not understand macros and when you should and should not use them (generally, you should never use them, with very few exceptions). The macro is textually substitued in to your code. That means the array creation and initialization is happening every time you use ALPHA_ARRAY. This is terrible.
Basically, never use #define again (except for constants) until you have a much deeper grasp of what you're doing. In this case, you would create the array as taebot described:
NSArray* alphaArray = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
Next, if you are developing for a modern platform (10.5 or iPhone), you can use Fast Enumeration which is much easier and clearer to read:
-(void) findRedundant: (NSString *) aString {
NSArray* alphaArray = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
BOOL found = NO;
for ( NSString* stringFromArray in alphaArray ) {
if ([aString isEqualToString:stringFromArray]) {
found = YES;
break;
}
}
if ( found ) {
// do found
} else {
// do not found
}
}
And finally, you should go read through the documentation on NSArray and NSString to see what you can do for free, and then you'll find methods like containsObject that KiwiBastard pointed out, and you can rewrite your routine as:
-(void) findRedundant: (NSString *) aString {
NSArray* alphaArray = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
if ( [alphaArray containsObject: aString] ) {
// do found
} else {
// do not found
}
}
I'm not sure why your code above isn't working, but have you tried:
if ([ALPHA_ARRAY containsObject:aString])
which will return YES if aString exists otherwise NO
The #define looks odd to me. I think that each time you use ALPHA_ARRAY a different NSArray instance will be created. It would be cleaner to use the containsObject: method on NSArray:
NSArray* alphaArray = [NSArray arrayWithObjects: #"A", #"B", #"C", nil];
if (![alphaArray containsObject:aString]) {
// do something
}
Related
Here is my code:
-(NSArray*)buttons {
NSArray *buttons = %orig;
NSMutableArray *mutableItems = [NSMutableArray arrayWithArray:buttons];
[mutableItems objectAtIndex:2];
return mutableItems;
}
I am trying to remove an object from an NSArray, but that NSArray is in a Swift class. I am having no trouble getting anything else in that Swift class to change, but I can't remove any objects from the NSArray. I don't get any errors, but the changes I make simply don't have any effect. This is for a jailbreak tweak.
Replace [mutableItems objectAtIndex:2] with [mutableItems removeObjectAtIndex:2].
Full example:
-(NSArray*)buttons {
NSArray* buttons = #[#"a", #"b", #"c", #"d"];
NSMutableArray *mutableItems = [NSMutableArray arrayWithArray:buttons];
[mutableItems removeObjectAtIndex:2];
return mutableItems;
}
The mutableItems array will contain a,b,d.
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
for (NSDictionary *wine in sortedWines)
{
NSNumber *rate = [wine valueForKey:#"Rate"];
NSString *rateStr = [NSString stringWithFormat:#"%.f", [rate floatValue]];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:rateStr])
{
found = YES;
}
}
if (!found)
{[self.sections setValue:[[NSMutableArray alloc] init] forKey:rateStr];}
}
for (NSDictionary *wine in sortedWines)
{[[self.sections objectForKey:[NSString stringWithFormat:#"%.f", [[wine valueForKey:#"Rate"] floatValue]] ] addObject:wine];}
// Sort:
for (NSString *key in [self.sections allKeys])
{[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"Rate" ascending:NO]]];}
This code puts my wines in sections, but it won't sort them in descending order! Could it be because the NSNumber is transformed into NSString? I've tried to make a code using the NSNumber value:
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
for (NSDictionary *wine in sortedWines)
{
NSNumber *rate = [wine valueForKey:#"Rate"];
found = NO;
for (NSNumber *str in [self.sections allKeys])
{
if ([str isEqualToNumber:rate])
{
found = YES;
}
}
if (!found)
{[self.sections setValue:[[NSMutableArray alloc] init] forKey:rate];}
}
// Loop again to sort wines into their keys
for (NSDictionary *wine in sortedWines)
{[[self.sections objectForKey:[wine valueForKey:#"Rate"]] addObject:wine];}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"Rate" ascending:NO]]];}
But it gives a warning for
if (!found)
{[self.sections setValue:[[NSMutableArray alloc] init] forKey:rate];}
that says "Incompatible pointer types sending NSNumber ___strong to parameter of type NSString"
If I run the app it crashes with error -[__NSCFNumber localizedCaseInsensitiveCompare:]: unrecognized selector sent to instance 0x1e085810
What do I have to change to make it work and sort the sections in descending order? Thanks.
I don't know if the default selector for sortDescriptorWithKey:ascending: is now caseInsensitiveCompare:, I'm pretty sure it used to be just compare:. In any case, you can use sortDescriptorWithKey:ascending:selector:, instead, and pass compare: for the selector. I think that should fix your second error. Still not sure why you're getting that first error.
You would do much better (and we'd understand you better) if you formatted your code for legibility. Eg:
// Loop again to sort wines into their keys
for (NSDictionary *wine in sortedWines) {
NSArray* section = [self.sections objectForKey:[wine valueForKey:#"Rate"]];
[section addObject:wine];
}
// Sort each section array
NSArray* sortDescriptorArray = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"Rate" ascending:NO]];
for (NSString *key in [self.sections allKeys]) {
NSArray* section = [self.sections objectForKey:key];
[section sortUsingDescriptors:sortDescriptorArray];
}
Among other things, this makes debugging much simpler since you can stop and dump the section arrays.
Or, if you really liked it better the other way, I can highly recommend that you learn APL or LISP instead.
I'm a developer from Python world used to using exceptions. I found in many places that using exceptions is not so wise here, and did my best to convert to NSErrors when needed. but then I encounter this:
NSMutableArray *results;
for (NSDictionary *dict in dicts)
{
// Memory management code omitted
SomeModel *model = [[SomeModel alloc] init];
model.attr1 = [[dict objectForKey:#"key1"] integerValue];
model.attr2 = [[dict objectForKey:#"key2"] integerValue];
model.attr3 = [[dict objectForKey:#"key3"] integerValue];
model.attr4 = [[dict objectForKey:#"key4"] integerValue];
[results addObject:model];
}
with some of the objects in dict containing NSNull, which would result an "unrecognized selector" exception. In that case, I want to drop that datum completely. My first instinct is to wrap the whole content of the for block into a #try-#catch block:
NSMutableArray *results;
for (NSDictionary *dict in dicts)
{
#try
{
SomeModel *model = [[SomeModel alloc] init];
model.attr1 = [[dict objectForKey:#"key1"] integerValue];
model.attr2 = [[dict objectForKey:#"key2"] integerValue];
model.attr3 = [[dict objectForKey:#"key3"] integerValue];
model.attr4 = [[dict objectForKey:#"key4"] integerValue];
[results addObject:model];
}
#catch(NSException *exception)
{
// Do something
}
}
But is this a good approach? I can't come up with a solution without repeating checks on each variable, which is really ugly IMO. Hopefully there are alternatives to this that haven't occur to me. Thanks in advance.
The proper Objective-C way to do this would be:
for (NSDictionary *dict in dicts)
{
if (! [dict isKindOfClass:[NSDictionary class]])
continue;
// ...
}
Testing if a receiver can respond to a message before sending it is a typical pattern in Objective-C.
Also, take note that exceptions in Objective-C are always a programmer error and are not used for normal execution flow.
Many people use a category on NSDictionary for these cases:
- (id)safeObjectForKey:(id)aKey
{
id obj = [self objectForKey:aKey];
if ([obj isKindOfClass:[NSNull class]])
{
return nil;
}
return obj;
}
You still need to make sure, that your dict is an actual dictionary instance.
In the end I decided to solve the problem using KVC. Something like this:
- (id)initWithPropertyDictionary:(NSDictionary *)dict
lookUpTable:(NSDictionary *)keyToProperty
{
self = [self init];
for (NSString *key in dict)
{
NSString *propertyName;
if ([keyToProperty objectForKey:key])
propertyName = [keyToProperty objectForKey:key];
else
propertyName = key;
if ([[dict objectForKey:key] isKindOfClass:[NSNull class]])
{
[self release];
return nil;
}
else
{
[self setValue:[dict objectForKey:key] forKey:propertyName];
}
}
}
The setback of this resolution is that I'll have to use NSNumber for my properties, but for JSON data there is really no distinction between floating numbers and integers, so this is fine.
And if you really want primitive types, you can couple this method with custom setters that converts those NSNumbers into appropriate types.
With this, all you need to do is check for nil before adding the object into the array. Much cleaner everywhere except the model class.
Thanks to jaydee3 for inspiring me to focus on changing the model class.
hi at all ,I've this code :
+(NSArray *)splatterUrls
{
NSString *jsonString = [ ApiMethod jsonOfStores];
NSDictionary *results =[jsonString objectFromJSONString];
NSArray *movieArray = [results objectForKey:#"Seasons"];
//int i=0;
// Search for year to match
for (NSDictionary *movie in movieArray)
{
NSNumber *idSplatterMovie = [movie objectForKey:#"Id"];
// NSLog(#" %#", idSplatterMovie );
NSArray *try = [movie objectForKey:#"Episodes"];
// NSLog(#"%#", try);
for (NSDictionary *op in try)
{
if([idSplatterMovie integerValue] == 46)
{
//i++;
NSArray *movieArrayString = [op objectForKey:#"Url"];
// NSLog(#" %#", movieArrayString);
return movieArrayString;
}
}
}
}
I want to return movieArrayString with all his objects and how many object contains in it. I think that I should use this method : + (id)arrayWithObjects:(const id *)objects count:(NSUInteger)count. It's possible? If yes, can you tell me how can use it?
Thank you so much!
by the way , i have to call splatterUrls method and implement in home.m that it is :
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *urlSplatter= [GetSplatterUrlsMovie splatterUrls];
NSLog(#" %#", urlSplatter);
}
Looks good as it is to me.
Do this to return your movies array, array will be equal to your movies array:
NSArray *array = [self splatterUrls];
Then to get the count/number of objects in your array do this, i is equal to the number of objects in the array:
int i = [array count];
What is the problem ??
You return a NSarray ... call the method count on your NSarray object!
So I was playing with something where the class type of the arg is unknown until runtime.
like this:
- (NSNumber *)doWhatever:(id)arg
{
// this ALWAYS FAILS
if ([arg isKindOfClass:[NSNumber class]]) {
return arg;
}
else {
// what was it???
NSLog("arg klass=%#", [arg class]); // prints NSCFNumber
}
// This check works correctly.
if ([arg isKindOfClass:[NSArray class]]) {
for (id x in arg) {
NSNumber *result = [self doWhatever:x];
if (result) {
return result;
}
}
}
return nil;
}
- (void)someMethod
{
NSArray *myArray = [NSArray arrayFromObjects:[NSNumber numberWithInt:3], nil]];
NSNumber *myNum = [self doWhatever:myArray];
NSLog(#"myNum=%#", myNum);
}
This is obviously a contrived example of what I'm trying to do.
The point is this never works b/c the class of "arg" always appears as NSCFNumber, and I can't figure out a way to check for that.
Any way to make it less confusing to detect whether an arbitrary value in an array is an integer or not?
UPDATE:
Thanks #chuck, #omz, and #Nikita Leonov for your help. What I posted here originally was just a simplification of the problem I was having and wrote it here without running it first. That code once updated to remove the errors (see below) runs fine actually.
The mistake I made in my real code that I was having trouble with was equally silly--I was passing the array back in to "doWhatever" instead of the item at array's index, which is why I was having problems.
Thanks for trying to help, however misguided my question was.
Sorry for wasting everybody's time!
Corrected code that runs as desired:
- (NSNumber *)doWhatever:(id)arg
{
// this NOW WORKS
if ([arg isKindOfClass:[NSNumber class]]) {
return arg;
}
else {
// what was it???
NSLog(#"arg klass=%#", [arg class]); // prints NSCFNumber
}
// This check works correctly.
if ([arg isKindOfClass:[NSArray class]]) {
for (id x in arg) {
NSNumber *result = [self doWhatever:x];
if (result) {
return result;
}
}
}
return nil;
}
- (void)someMethod
{
NSArray *myArray = [NSArray arrayWithObjects:
[NSNumber numberWithInt:1],
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:4],
nil];
NSNumber *myNum = [self doWhatever:myArray];
NSLog(#"myNum=%#", myNum);
}
NSCFNumber is a subclass of NSNumber. As long as you're using isKindOfClass: rather than isMemberOfClass: or [arg class] == [NSNumber class], it should work. If not, your problem is elsewhere.