Trouble filtering array of custom objects by an NSNumber using NSPredicate

独自空忆成欢 提交于 2021-01-29 13:28:25

问题


This should be straightforward but something is preventing me from filtering an array of custom objects by NSNumber using NSPredicate. Perhaps it has something to do with the datatype when converting from JSON but I can't figure it out.

I download data from a JSON in an array of custom Objects that look like:

{"hid":"47","public":"1"}

The code to parse the JSON looks like:

 if (feedElement[@"public"] && ![feedElement[@"public"] isEqual:[NSNull null]]) {
            newMyObject.pub = feedElement[@"public"]== nil ? @"0" : feedElement[@"public"];}

The object looks like:

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyObject : NSObject
@property (nonatomic, retain) NSNumber * hid; 
@property (nonatomic, retain) NSNumber * pub;
@end
NS_ASSUME_NONNULL_END

The objects are placed in an NSArray * myObjects

My NSPredicate and filter code looks like:

NSPredicate *pubPred = [NSPredicate predicateWithFormat:@"pub == 1"];
NSArray *filteredArray = [myObjects filteredArrayUsingPredicate:pubPred];

When I log [myObjects valueForKey:@"pub"], it logs as 1,1,1, etc. so I know that the values for pub are all 1 however the resulting filteredArray is empty.

What could be wrong with my code?

Thanks for any suggestions.

Edit: I changed public to pub in the object in case public was a reserved word but it did not change anything


回答1:


With sample code : {"hid":"47","public":"1"}

newMyObject.pub = feedElement[@"public"]== nil ? @"0" : feedElement[@"public"];

In case of public value is present in JSON, you'll set pub property with feedElement[@"public"], which means, it will be @"1" (with the sample), which means you'll put a NSString.
In case of public value is present in JSON, you'll set pub property with @"0" which means you'll put a NSString.

It doesn't matter if it's declared @property (nonatomic, retain) NSNumber * pub;, you are setting a NSString, not a NSNumber.

Want some code testing?

@interface MyObject : NSObject

@property (nonatomic, retain) NSNumber *hid;
@property (nonatomic, retain) NSNumber *pub;

+(void)test;
@end

And

@implementation MyObject

-(id)initWithPub:(NSNumber *)pub andHID:(NSNumber *)hid {
    self = [super init];
    if (self) {
        self.pub = pub;
        self.hid = hid;
    }
    return self;
}

-(NSString *)description {
    return [NSString stringWithFormat:@"%@ pub: %@ - hid: %@", [super description], [self pub], [self hid]];
}

+(NSArray *)arrayList {
    return @[[[MyObject alloc] initWithPub:@1 andHID:@3], //Here with real NSNUmbner
             [[MyObject alloc] initWithPub:@"1" andHID:@"2"]]; //Here with NSString instead
}

+(void)test {
    NSArray *list = [MyObject arrayList];

    NSPredicate *predicateNSNumber = [NSPredicate predicateWithFormat:@"pub == %@", @1];
    NSArray *filteredNSNumber = [list filteredArrayUsingPredicate:predicateNSNumber];
    NSLog(@"Filtered-NSNumber: %@", filteredNSNumber);

    NSPredicate *predicateNSString = [NSPredicate predicateWithFormat:@"pub == %@", @"1"];
    NSArray *filteredNSString = [list filteredArrayUsingPredicate:predicateNSString];
    NSLog(@"Filtered-NSString: %@", filteredNSString);
}

Output:

$> Filtered-NSNumber: (
    "<MyObject: 0x60000024cba0> pub: 1 - hid: 3"
)
$> Filtered-NSString: (
    "<MyObject: 0x60000024d1e0> pub: 1 - hid: 2"
)

You can state: "Yeah, but you have a warning in [[MyObject alloc] initWithPub:@"1" andHID:@"2"]: Incompatible pointer types sending 'NSString *' to parameter of type 'NSNumber *', yes, I have. You should have it too.

In fact, you'll have it too if you wrote your ternary if :

if (feedElement[@"public"] == nil) {
    newMyObject.pub = @"0"; //Incompatible pointer types assigning to 'NSNumber * _Nonnull' from 'NSString *'
} else {
    newMyObject.pub = feedElement[@"public"]; //Here, no warning, because it's hope you know what you are doing and setting a NSNumber here.
}

What about using this to your code to check:

for (MyObject *anObject in list) {
    NSLog(@"Testing: %@", anObject);
    if ([[anObject pub] isKindOfClass:[NSString class]]) {
        NSLog(@"Pub is a String");
    } else if ([[anObject pub] isKindOfClass:[NSNumber class]]) {
        NSLog(@"Pub is a NSNumber");
    }
    NSLog(@"Attempt to call -stringValue which is a NSNumber method, not a NSString one");
    NSLog(@"Attempt Call: %@\n", [[anObject pub] stringValue]);
}

You should get a -[__NSCFConstantString stringValue]: unrecognized selector sent to instance error, because it's really a NSString, not a NSNumber.

Solutions:

You need to fix your parsing, or change the type of property in MyObject.

With keeping the property as a NSNumber:

if ([feedElement[@"public"] isKindOfClass:[NSString class]]) {
     self.pub = @([feedElement[@"public"] integerValue]); //Transform it into a NSInteger, then into a NSNumber with @(someInteger)
} else ([feedElement[@"public"] isKindOfClass:[NSNumber class]]) {
    self.pub = feedElement[@"public"]; //It's already a NSNumber instance
} else {
    self.pub = @(0); //Default value because unknown class or not present 
}


来源:https://stackoverflow.com/questions/61128529/trouble-filtering-array-of-custom-objects-by-an-nsnumber-using-nspredicate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!