Better alternative for checking existence of NSDictionary's key in NSArray? - objective-c

I have an NSArray of NSDictionaries. I need to check if there's at least one occurrence of an object for a key of the NSDictionary in the NSArray. I do this by using
int i;
for (i=0;i< [myArray count];i++)
{
if ([[[myArray objectAtIndex: i] objectForKey: myKey] isEqualToString: myString]) {
found = YES;
break;
} else {
found = NO;
}
}
But I have a suspicion that there's a better/faster alternative for it...
Thanks

Yes. Use "fast enumeration", commonly also known as for-in loop:
for (NSDictionary* dict in myArray) {
Also, to compare NSString's, use -isEqualToString:.
if ([[dict objectForKey: myKey] isEqualToString:myString]) {
That said, there is no algorithmic improvement to this (i.e. this method is already the best.)

This is as fast as you can get it with your current data structures. You're doing an O(1) lookup for each dictionary in the array. If you have a huge number of dictionaries, this might get expensive, so you could consider (depending very much on the semantics of your data) keeping a separate lookaside NSSet that contains the set of string objects comprising all the values in the dictionaries. Then you can check once in that set for existence.
Tell us more about the form of the data for more insight...
Also be careful with the == operator with NSStrings. If you're actually checking to see whether the text of the string is equal, you should use -isEqualToString: instead, as your form will just do a reference comparison.

You should use fast enumeration, which will iterate through the objects using a C array behind the scenes. Right now, you're calling both -objectAtIndex: and -count each time through the loop.
You might also check out NSPredicate if myKey is a string. My gut tells me it would be slower, but you never know if it might benefit from internal optimization for NSDictionary:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# LIKE %#", myKey, myString];
BOOL found = ([[myArray filteredArrayUsingPredicate:predicate] count] > 0);

You could be much more succinct with Key-Value Coding:
[[myArray valueForKey:myKey] containsObject:myString];
It's not necessarily faster (I suspect it will be slower), but speed is not always the primary concern. Whether speed is critical in a particular case is a matter for profiling to decide.

Using == to check string equality might cause unexpected behavior, because you're actually comparing pointers (that can be okay if you're sure that you're dealing with a single string object pointed to by two pointers). isEqualToString: is probably what you want instead.
You can use "fast enumeration" to simplify things slightly:
bool found = NO;
for (NSDictionary *dict in myArray) {
found = [[dict objectForKey:myKey] isEqualToString:myString];
if (found)
break;
}
It's only "faster" in the sense that it's fewer words to write; the execution speed is the same.

After receiving your object, You can check that your received object is "NSArray" or "NSDictionary" or "NSString" etc. You can use following code to verify your object.
if([obj isKindOfClass:[NSArray class]]){
NSLog(#"IS NSArray");
}
else if([obj isKindOfClass:[NSDictionary class]]){
NSLog(#"Is NSDictionary");
}
else
{
NSLog(#"Other");
}

With fast enumeration
BOOL found;
for (NSDictionary *dict in array) {
if ([[dict objectForKey:#"YOURKEY"] isEqualToString:#"YOURVALUE"]) {
found = YES;
break;
} else {
found = NO;
}
}

Related

Obj-C: using mutable and returning non mutable classes in methods

In objective-C I find myself creating alot of Mutable objects and then returning them as non mutable objects. Is the way I am doing it here, simply returning the NSMutableSet as an NSSet a good practice? I was thinking maybe I should specify that i make a copy of it.
/** Returns all the names of the variables used in a given
* program. If non are used it returns nil */
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet* variablesUsed = [[NSMutableSet alloc]init];
if ([program isKindOfClass:[NSArray class]]) {
for (NSString *str in program)
{
if ([str isEqual:#"x"] || [str isEqual:#"y"] || [str isEqual:#"a"] || [str isEqual:#"b"])
[variablesUsed addObject:str];
}
}
if ([variablesUsed count] > 0) {
return variablesUsed;
} else {
return nil;
}
}
If I were you, I would do it this way.
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSSet *variablesUsed;
if ([program isKindOfClass:[NSArray class]]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF = 'x' or SELF = 'y' or SELF = 'z'"];
variablesUsed = [NSSet setWithArray:[program filteredArrayUsingPredicate:predicate]];
}
int count;
return (count = [variablesUsed count]) > 0 ? variablesUsed : nil;
}
I find using predicate to filter array quite comprehensive and easy. Rather than dealing with creating a new mutable type and then testing certain condition, adding until the loop; in this scenario, it seems to be easier to use predicate. Hope this helps you.
It depends how much safety you require. If you return the object as an NSSet it will still be an NSMutableSet, so it could easily be cast back to one and modified.
Certainly, if you're creating a public API, I'd recommend returning a copy. For in internal project, perhaps the method signature already makes the intention clear enough.
Its, worth noting that, generally the performance impact of returning a copy is negligible - copying an immutable instance is effectively free whereas each copy sent to a mutable-passing-as-immutable will create another copy. So I would say its good practice to default to.
No. This is an absolutely correct OOP approach (it takes advantage of polymorphism). Every NSMutableSet is a proper NSSet. Don't copy superfluously.
Not a full answer here, consider NSProxy's one, but I want to clarify something.
In your case you create your object from scratch, and you don't set any ivar to point to that object. In my opinion in a good percentage of cases you don't need to make a copy of the mutable object returned. But if there is a good reason to deny the class client from mutating the class, then you should copy the variable.
Consider a property like this:
#property (nonatomic,assign) NSSet* set;
The class client could do this:
NSMutableSet* set= ... ; // inizialized to some value
classInstance.set= set;
// Mutate the set
Once mutated the set it could make the class be in an inconsistent state.
That's why when I have a property with the type of a class that has also a mutable version, I always put copy instead of assign in the property.

NSDictionary case insensitive objectForKey:

NSDictionary has objectForKey but it's case-sentive for keys. There is No function available like
- (id)objectForKey:(id)aKey options:(id) options;
where in options you can pass "NSCaseInsensitiveSearch"
To get key's from NSDictionary which is case-insesitive one can use the following code written below.
You need to add Category of NSDictionary Class with this functionality
- (id)objectForCaseInsensitiveKey:(NSString *)key {
NSArray *allKeys = [self allKeys];
for (NSString *str in allKeys) {
if ([key caseInsensitiveCompare:str] == NSOrderedSame) {
return [self objectForKey:str];
}
}
return nil;
}
This isn't included for a couple of reasons:
NSDictionary uses hash equality, and for pretty much any good hashing algorithm, any variation in the source string results in a different hash.
More importantly, NSDictionary keys are not strings. Any object that conforms to NSCopying can be a dictionary key, and that includes a whole lot more than strings. What would a case-insensitive comparison of an NSNumber with an NSBezierPath look like?
Many of the answers here offer solutions that amount to transforming the dictionary into an array and iterating over it. That works, and if you just need this as a one-off, that's fine. But that solution is kinda ugly and has bad performance characteristics. If this were something I needed a lot (say, enough to create an NSDictionary category), I would want to solve it properly, at the data structure level.
What you want is a class that wraps an NSDictionary, only allows strings for keys and automatically lowercases keys as they are given (and possibly also remembers the original key if you need a two-way mapping). This would be fairly simple to implement and is a much cleaner design. It's too heavy for a one-off, but if this is something you're doing a lot, I think it's worth doing cleanly.
The correct answer is that you should use case-folded keys as dictionary keys. This is not the same as converting them to upper or lower case and it won't destroy the O(1) average case search/insert complexity.
Unfortunately, Cocoa doesn't seem to have an appropriate NSString method to case-fold a string, but Core Foundation has CFStringFold() which you can use for that purpose. Let's write a short function to do the necessary work:
NSString *foldedString(NSString *s, NSLocale *locale)
{
CFMutableStringRef ret = CFStringCreateMutableCopy(kCFAllocatorDefault, 0,
(__bridge CFStringRef)s);
CFStringNormalize(ret, kCFStringNormalizationFormD);
CFStringFold(ret, kCFCompareCaseInsensitive, (__bridge CFLocaleRef)locale);
return (__bridge_transfer NSString *)ret;
}
Note that the locale argument is important. If you specify NULL, you will get the current system locale. This will be fine in most cases, but Turkish users might be surprised that "I" matches "i" rather than "ı". You might therefore want to pass [NSLocale currentLocale], and if you're saving the results you might also want to save the locale identifier and create the locale from that.
So, when adding to the dictionary, you now need to do
[dict setObject:obj forKey:foldedString(myKey, locale)];
and to look up again
[dict objectForKey:foldedString(myKey, locale)];
One final observation is that you might wish to store the case-folded keys alongside the original values, then you don't have to fold them on every access to the dictionary.
In the code written below, I search for a actual key for a input key. So , if input key=#"naMe" then the actual key=#"name".
NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:#"John",#"Name",#"123456",#"empId", nil];
NSString *key=#"naMe";
NSString *name=[dic objectForKey:key];
if(name==nil){
NSPredicate *searchPred=[NSPredicate predicateWithFormat:#"self LIKE[cd] %#",key];
NSArray *searchedKeys=[[dic allKeys] filteredArrayUsingPredicate:searchPred];
if(searchedKeys.count>0){
name=[dic objectForKey:[searchedKeys objectAtIndex:0]];
}
}
NSLog(#"Name = %#",name);
Many answers are correct, but here's a more example:
NSDictionary* dict= #{ #"hello" : #"Hey" };
NSArray* keys= [dict allKeys];
NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop)
{
if( [obj caseInsensitiveCompare: #"Hello"]==NSOrderedSame)
{
*stop= YES;
return YES;
}
else
{
return NO;
}
}];
Personally I find this method easier, but everyone has his programming style.
EDIT
A less readable but shorter solution:
NSDictionary* dict= #{ #"hello" : #"Hey" };
NSArray* keys= [dict allKeys];
NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop)
{
return *stop= [obj caseInsensitiveCompare: #"Hello"]==NSOrderedSame ;
}];
If you are only storing into, and retrieving from, the NSDictionary in one place (maybe two or three), you could use
[myString lowercaseString]
in both. The more rigorous answers are useful if the dictionary object is used all over your code.

Why Doesn't NSArray return NULL for nonexistent indexes?

The way I understand it one of the things special about Objective C is that you can send messages to NULL and it will just ignore them instead of crashing.
Why is it that NSArray doesn't just return a NULL object if the index requested is out of bounds instead of causing a NSRangeException?
What I would expect from Objective C and NSArray is the following.
NSArray *array = [NSArray arrayWithObjects:#"Object 1", #"Object 2", #"Object 3", nil];
for (int i = 0; i < 5; i++) {
NSString *string = [array objectAtIndex:i];
if (string) {
NSLog(#"Object: %#",string);
}
}
Allowing me to access indexes of the array with don't contain objects and simply returning NULL. Then I can check if the object exists. I can do this in other places such as checking if the object has been instantiated.
NSArray *array;
if (!array) {
array = [NSArray array];
}
I realize this is a theory based question but I'm curious :)
Messages to nil is a language level feature, not a framework API feature.
That NSArray (and other classes) throw an exception to indicate programmer error is a conscious design decision that was made when the API was designed (~1993). As both Kevin and Richard indicate, nil eats message is not erroneous in anyway whereas out of bounds is very much a case of erroneous input.
Where would you draw the line? Should substringWithRange: accept any old input? How about setObject:atIndex:?
By making NSArray #throw on bounds exceptions for indexOfObject: it makes the API consistent; any index (or range) that is out of bound will #throw.
You could use an NSMutableDictionary to accomplish what you're trying to do. Set the keys to be NSNumber indexes. Invoking objectForKey on a nonexistent key (out of our imaginary bounds or removed) will return nil.

Add object to array only if the object is not in already

I want to add object to array only if the array already does not contain that object.
How to do opposite of containsObject method in NSArray ?
Use an NSMutableOrderedSet, whose addObject: method does exactly what you want:
Appends a given object to the mutable ordered set, if it is not already a member.
Here's how I'd do it:
if (![myArray containsObject:objectToAdd]){
[myArray addObject:objectToAdd];
}
More detail here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
Note that because the containsObject method queries every object in the array there are some performance considerations when using it on larger arrays.
if your object is of NSString* type you can do fast enumeration like this
BOOL found = NO;
for(NSString *object in YourArray)
{
if([object isEqualtoString:#"My text"])
{
found = YES;
}
}
if(!found)
{
//addObject
}
there are many isEqual methods in objective-c for different data types

Check whether an object is an NSArray or NSDictionary

As per subject, how can I check whether an object is an NSArray or NSDictionary?
if([obj isKindOfClass:[NSArray class]]){
//Is array
}else if([obj isKindOfClass:[NSDictionary class]]){
//is dictionary
}else{
//is something else
}
Try
[myObject isKindOfClass:[NSArray class]]
and
[myObject isKindOfClass:[NSDictionary class]]
Both of these should return BOOL values. This is basic use of the NSObject method:
-(BOOL)isKindOfClass:(Class)aClass
For a bit more information, see this answer here:
In Objective-C, how do I test the object type?
Consider the case when you're parsing data from a JSON or XML response. Depending on the parsing library you are using, you may not end up with NSArrays or NSDictionaries. Instead you may have __NSCFArray or __NSCFDictionary.
In that case, the best way to check whether you have an array or a dictionary is to check whether it responds to a selector that only an array or dictionary would respond to:
if([unknownObject respondsToSelector:#selector(lastObject)]){
// You can treat unknownObject as an NSArray
}else if([unknownObject respondsToSelector:#selector(allKeys)]){
// You can treat unknown Object as an NSDictionary
}
Just in case anyone comes late to this party looking for a Swift equivalent, here you go. It's a lot more elegant than the Objective-C version, IMHO, because not only does it check the types, but it casts them to the desired type at the same time:
if let arrayVersion = obj as? NSArray {
// arrayVersion is guaranteed to be a non-`nil` NSArray
} else if let dictionaryVersion = obj as? NSDictionary {
// dictionaryVersion is guaranteed to be a non-`nil` NSDictionary
} else {
// it's neither
}