问题
I'm trying to write an instance method for a Card
class that compares a single card against an array. The class has some properties like: shape
and color
. The otherCards
array is filled with other instances of this class, that also have their shape
s and color
s.
Now, I want to write a method that can check all of these attributes separately. How can I pass in a particular attribute, as in: [allAttributesIsEqual:otherCards compareWith: self.shape]
? So I can pass in self.shape
or self.color
when actually comparing?
- (BOOL)allAttributesIsEqual: (NSArray *)otherCards
{
//self.shape is equal to othercards.shape
}
回答1:
You can't just pass in self.shape
, because that will give you the value of the property. Thanks to some of Cocoa/ObjC's dynamite, however, you can pass in the name of a property (or method) and get the results later.
The clever (dare I say, perhaps even "Pythonic") way:
// The name of the property we're interested in.
NSString * key = @"color";
// Get the values of that property for all the Cards in the array, then
// collapse duplicates, because they'll give the same results when comparing
// with the single card.
NSSet * vals = [NSSet setWithArray:[arrayOfCards valueForKey:key]];
// Now, if the set has only one member, and this member is the same
// as the appropriate value of the card we already have, all objects
// in the array have the same value for the property we're looking at.
BOOL colorIsEqual = ([vals count] == 1 && [vals containsObject:[myCard valueForKey:key]]);
Then your method can look like this:
- (BOOL)allOtherCards: (NSArray *)otherCards haveEqualAttribute: (NSString *)key;
Dan F's suggestion to implement - (BOOL)<#property#>Equal: (NSArray *)otherCards;
for each property you're interested in is not at all a bad idea, however. Of course, each of these could call through to the base "clever" version.
回答2:
The idea is that you (as the Card class) know what it means for two instances to be "equal". It sounds like in your case, two Cards are equivalent if their color and shape properties match. Start by implementing -isEqual:
(along with -hash
) on your custom Card class. This is the standard way of having an object expose a notion of whether it is the same as some other object. You can implement this however you need. Within this isEqual
method, you can check all of the relevant properties:
- (BOOL)isEqual:(id)otherObject
{
if (![otherObject isKindOfClass:[self class]) {
return NO;
}
Card * otherCard = (Card *)otherObject;
// now compare the attributes that contribute to "equality"
return ([self.shape isEqual:otherCard.shape] && [self.color isEqual:otherCard.color]);
}
Now, once your custom object supports this -isEqual:, you can check all the cards in the array to see if any are equal to the candidate card. You could do the loop yourself and use the -isEqual:, but the nice thing about doing this in the system standard way is that you can also use system provided convenience methods to check for collection membership, like:
if ([myCardList containsObject:candidateCard]) {
// one of the cards compared as "equal"
}
If you would prefer to do this as you request in a method on your class, you could then structure it like so:
- (BOOL)isRepresentedInArray:(NSArray *)arr
{
return [arr containsObject:self];
}
来源:https://stackoverflow.com/questions/14903020/comparing-one-property-of-an-instance-against-an-array-of-other-instances