Is there a native function that checks if a value is inside of an enum? - objective-c

I'm trying to put UILabels inside of an NSDictionary, I'm using tags as key, but the problem is, not all of the labels inside of the view is needed, the tags of the UILabels that is needed is also inside of an enum.
So what I want to do is, check the tag if it exist inside of the enum then add it to dictionary with tag as key.
for (NSObject *obj in [self.formView subviews]) {
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)obj;
// Here is where I want to add the check before I do this line
labelDict[[NSString stringWithFormat:#"%d",label.tag]] = label;
}
}
For Future Readers:
If you are also iterating on an NSArray type of object like the code above, you should use the NSArray function enumerateObjectsUsingBlock instead, this is the answer, doesn't it look more pretty:
[self.formView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)obj;
if ( label.tag >= TEXTFIELDTYPE_MIN_VAL && label.tag <= TEXTFIELDTYPE_MAX_VAL ) {
labelDict[[NSString stringWithFormat:#"%d",label.tag]] = label;
}
}
}];

Objective-C enums are inherited from C enums and cannot be reflected at runtime. Without abusing debug symbols (which would be an overly complicated task for a slow and unreliable result), I believe that it would be impossible to come up with a function that would tell you if an arbitrary value is a member of an arbitrary enum.
One possible workaround would be to create a NSSet that contains all the enum values you have, and check if your label's tag exists within that set. Otherwise, if your enum is sequential, you can check that the tag is between your enum's minimum value and maximum value.

Try this, instead of for loop of all subviews, run for loop of your enum values,
hopefully the enum values are in sequence then only this below code would work:
for(NSInteger tagVal = enum.firstEnum; tagVal <= enum.lastEnum; tagValue++) {
NSObject *obj = [self.formView viewWithTag:tagVal];
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)obj;
// Here is where I want to add the check before I do this line
labelDict[[NSString stringWithFormat:#"%d",label.tag]] = label;
}
}

Try this
if ([[dict allKeys] containsObject:lbl]) {
}

Related

distinctUnionOfObjects case insensitive

I have an array of NSDictionary. Each array item has a key named "Name". Now I want to remove duplicate entries based on this name value.
This work perfectly:
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects.Name"];
The problem are that result array still contains duplicates string, based on the case. Ex: [#"Franck", "franck"]
How can I remove these duplicate?
Thanks
You could try to do this
// in your class implementation
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[self class]]) {
return NO;
}
typeof(self) obj = (typeof(self))object;
return ([self.Name caseInsensitiveCompare:obj.Name] == NSOrderedSame);
}
- (NSUInteger)hash
{
return [[self.Name lowercaseString] hash];
}
// and then
NSSet *distinctObjects = [[NSSet alloc] initWithArray:array];
NSArray *result = distinctObjects.allObjects;
Alternatively you could customise this KVC collection operator by swizzling valueForKeyPath: to parse your custom DSL, possibly winding up with something like
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects[caseInsensitive].Name"];
which doesn't seem to be a good idea for me, but it certainly a viable solution to your problem.

Create a NSSet from NSArray based on property

How does one create a NSSet of objects from an array based on a property.
e.g. Array of objects, each with a strong reference to a type property, and multiple occurrences of each type exist in the array. How can this be turned into an NSSet holding a single object of each type.
NSSet *distinctSet = [NSSet setWithArray:[array valueForKeyPath:#"#distinctUnionOfObjects.property"]];
A dictionary essentially has this functionality already. Its keys are a set, so you can create the dictionary to hold the objects, keyed by whatever attribute you're interested in:
[NSDictionary dictionaryWithObjects:arrayOfObjects
forKeys:[arrayOfObjects valueForKey:theAttribute]];
If you ask the dictionary for allValues now, you have only one object for each attribute. I should mention that with this procedure, the later objects will be kept in favor of earlier. If the order of your original array is significant, reverse it before creating the dictionary.
You can't actually put those objects into an NSSet, because the NSSet will use the objects' isEqual: and hash methods to determine whether they should be members, rather than the key attribute (of course, you can override these methods if this is your own class, but that would likely interfere with their behavior in other collections).
If you really really feel that you need a set, you will have to write your own class. You can subclass NSSet, but conventional wisdom is that composition of Cocoa collections is far easier than subclassing. Essentially, you write a class which covers any set methods you're interested in. Here's a (quite incomplete and totally untested) sketch:
#interface KeyedMutableSet : NSObject
/* This selector is performed on any object which is added to the set.
* If the result already exists, then the object is not added.
*/
#property (assign, nonatomic) SEL keySEL;
- (id)initWithKeySEL:(SEL)keySEL;
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL;
- (void)addObject:(id)obj;
- (NSArray *)allObjects;
- (NSArray *)allKeys;
- (BOOL)containsObject:(id)obj;
- (NSUInteger)count;
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block;
// And so on...
#end
#import "KeyedMutableSet.h"
#implementation KeyedMutableSet
{
NSMutableArray * _objects;
NSMutableSet * _keys;
}
- (id)initWithKeySEL:(SEL)keySEL
{
return [self initWithArray:nil usingKeySEL:keySEL];
}
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL
{
self = [super init];
if( !self ) return nil;
_keySEL = keySEL;
_objects = [NSMutableArray array];
_keys = [NSMutableSet set];
for( id obj in initArray ){
[self addObject:obj];
}
return self;
}
- (void)addObject:(id)obj
{
id objKey = [obj performSelector:[self keySEL]];
if( ![keys containsObject:objKey] ){
[_keys addObject:objKey];
[_objects addObject:obj];
}
}
- (NSArray *)allObjects
{
return _objects;
}
- (NSArray *)allKeys
{
return [_keys allObjects];
}
- (BOOL)containsObject:(id)obj
{
return [_keys containsObject:[obj performSelector:[self keySEL]]];
}
- (NSUInteger)count
{
return [_objects count];
}
- (NSString *)description
{
return [_objects description];
}
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block
{
for( id obj in _objects ){
BOOL stop = NO;
block(obj, &stop);
if( stop ) break;
}
}
#end
NSMutableSet* classes = [[NSMutableSet alloc] init];
NSMutableSet* actualSet = [[NSMutableSet alloc] init];
for(id object in array) {
if([classes containsObject:[object class]] == NO) {
[classes addObject:[object class]];
[actualSet addObject:object];
}
}
You would use:
NSSet* mySetWithUniqueItems= [NSSet setWithArray: yourArray];
This should work regardless of the type of objects in your array and would populate the NSSet with only one occurence of any duplicate objects in your array.
I hope this helps.
Update:
Next best thing: is use concatenation of class name and object property first then use the above method.
self.concatenatedArray=[NSMutableArray arrayWithCapacity:4];
for (TheClass* object in self.myArray)
[self.concatenatedArray addObject:[NSString stringWithFormat:#"%#-%#",[object class], object.theProperty]];
self.mySet=[NSSet setWithArray:self.concatenatedArray];
I am not sure what you will use the NSSet output for but you can probably modify the concatenation elements to have the information you need in the NSSet output.
I have created a simple library, called Linq to ObjectiveC, which is a collection of methods that makes this kind of problem much easier to solve. In your case you need the Linq-to-ObjectiveC distinct method:
NSSet* dictionary = [NSSet setWithArray:[sourceArray distinct:^id(id item) {
return [item type] ;
}]];
This returns a set where each item has a distinct type property.

How could I get the property name of the current UITextField a user is in as a string?

I am using the following method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
Before, in order to find the current text field I was in, I was able to write something like this:
if (textField == self.textPlaceID)
{
//Do something
}
Is there a way to grab the name of my textField as a string? I am not asking to take what the user has typed in that textField, I'd like to be able to just get the property name of if. I need it to then concatenate it with some other strings. I am working on making a dynamic method call.
UITextFields do not have "names". You can give your text field a tag and use that.
Also, note that (pointer == pointer) will only return true if you are referencing the same objects, not equivalent values.
Here is how to use the tag: in Interface Builder, give each text field a tag, or if you create your text fields programmatically, set textField.tag = someInt; I usually use macros to make the code more readable:
#define kNameTextField 2
#define kAddressTextField 3
...
if (textField.tag == kNameTextField) ...
With lots of fields like that, I prefer enums:
typedef enum {
kNameTextField = 2,
kAddressTextField,
kPhoneTextField // etc
} Fields;
You can get name of property (in your case textField property) using this code:
-(NSString *)propertyName:(id)property {
unsigned int numIvars = 0;
NSString *key=nil;
Ivar * ivars = class_copyIvarList([self class], &numIvars);
for(int i = 0; i < numIvars; i++) {
Ivar thisIvar = ivars[i];
if ((object_getIvar(self, thisIvar) == property)) {
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
break;
}
}
free(ivars);
return key;
}
Remember to import:
#import <objc/runtime.h>
Just call:
NSLog(#"name = %#", [self propertyName:self.textField]);
source
Primarily assign unique tags to each of the textFields (eg 1,2,3.....) avoid 0 for a textField tag
-(void)viewDidLoad{
NSDictionary *dictionary ; //declare it in .h interface
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"textPlaceID",[NSNumber numberWithInt:1], #"textPlaceID2",[NSNumber numberWithInt:2],nil]; // add textField as object and tag as keys
}
further..
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string{
NSString *textFieldPropertyName = [dictionary objectForKey:[NSNumber numberWithInt:textField.tag]];
}

NSFormatter for BOOL

I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).

Removing duplicates from array based on a property in Objective-C

I have an array with custom objects. Each array item has a field named "name". Now I want to remove duplicate entries based on this name value.
How should I go about achieving this?
I do not know of any standard way to to do this provided by the frameworks. So you will have to do it in code. Something like this should be doable:
NSArray* originalArray = ... // However you fetch it
NSMutableSet* existingNames = [NSMutableSet set];
NSMutableArray* filteredArray = [NSMutableArray array];
for (id object in originalArray) {
if (![existingNames containsObject:[object name]]) {
[existingNames addObject:[object name]];
[filteredArray addObject:object];
}
}
You might have to actually write this filtering method yourself:
#interface NSArray (CustomFiltering)
#end
#implementation NSArray (CustomFiltering)
- (NSArray *) filterObjectsByKey:(NSString *) key {
NSMutableSet *tempValues = [[NSMutableSet alloc] init];
NSMutableArray *ret = [NSMutableArray array];
for(id obj in self) {
if(! [tempValues containsObject:[obj valueForKey:key]]) {
[tempValues addObject:[obj valueForKey:key]];
[ret addObject:obj];
}
}
[tempValues release];
return ret;
}
#end
I know this is an old question but here is another possibility, depending on what you need.
Apple does provide a way to do this -- Key-Value Coding Collection Operators.
Object operators let you act on a collection. In this case, you want:
#distinctUnionOfObjects
The #distinctUnionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator.
NSArray *distinctArray = [arrayWithDuplicates valueForKeyPath:#"#distinctUnionOfObjects.name"];
In your case, though, you want the whole object. So what you'd have to do is two-fold:
1) Use #distinctUnionOfArrays instead. E.g. If you had these custom objects coming from other collections, use #distinctUnionOfArray.myCollectionOfObjects
2) Implement isEqual: on those objects to return if their .name's are equal
I'm going to get flak for this...
You can convert your array into a dictionary. Not sure how efficient this is, depends on the implementation and comparison call, but it does use a hash map.
//Get unique entries
NSArray *myArray = #[#"Hello", #"World", #"Hello"];
NSDictionary *uniq = [NSDictionary dictionaryWithObjects:myArray forKeys:myArray];
NSLog(#"%#", uniq.allKeys);
*Note, this may change the order of your array.
If you'd like your custom NSObject subclasses to be considered equal when their names are equal you may implement isEqual: and hash. This will allow you to add of the objects to an NSSet/NSMutableSet (a set of distinct objects).
You may then easily create a sorted NSArray by using NSSet's sortedArrayUsingDescriptors:method.
MikeAsh wrote a pretty solid piece about implementing custom equality: Friday Q&A 2010-06-18: Implementing Equality and Hashing
If you are worried about the order
NSArray * newArray =
[[NSOrderedSet orderedSetWithArray:oldArray] array]; **// iOS 5.0 and later**
It is quite simple in one line
NSArray *duplicateList = ...
If you don't care about elements order then (unordered)
NSArray *withoutDUP1 = [[NSSet setWithArray:duplicateList] allObjects];
Keep the elements in order then (ordered)
NSArray *withoutDUP2 = [[NSOrderedSet orderedSetWithArray:duplicateList] array];
Implement isEqual to make your objects comparable:
#interface SomeObject (Equality)
#end
#implementation SomeObject (Equality)
- (BOOL)isEqual:(SomeObject*)other
{
return self.hash == other.hash;
}
- (NSUInteger)hash
{
return self.name;///your case
}
#end
How to use:
- (NSArray*)distinctObjectsFromArray:(NSArray*)array
{
return [array valueForKeyPath:#"#distinctUnionOfObjects.self"];
}