I have a NSDictionary with a NSString and NSArray
I have to save only the NSArray in a variable without knowing the key.
Is this possible?
If I'm understanding you correctly, you have a dictionary that contains both an NSString and an NSArray, and you want to extract just the NSArray, without knowing what the key is.
One way to do that is to look through the dictionary with fast enumeration:
NSString *key;
for(key in someDictionary){
id someObject = [someDictionary objectForKey: key];
}
and then look at the objects to see which one is an NSArray:
if ([someObject isKindOfClass:[NSArray class]]) {
// do something with the array
}
(obligatory warning: explicitly checking an object's class is often a sign of a flawed design. In most cases, you should be checking for behavior (-respondsToSelector), not class identity)
Related
I want to get the class name of an object as what we are using.
That means now if I write this code
NSString *s = [NSString string];
NSLog(#"%#",[s class]);
The output is __NSCFConstantString
How can I get it as NSString itself ?
Note : NSString is just an example
I know __NSCFConstantString is correct. But my intention is to get like NSString. Is there any way to acheive this?
Give these a try, they'll output NSString. Keep in mind, the second set requires importing the Objective-C runtime header.
#import <objc/runtime.h>
NSString *string = #"I'm a string.";
NSLog(#"%#",NSStringFromClass([string classForCoder]));
NSLog(#"%#",NSStringFromClass([string classForKeyedArchiver]));
NSLog(#"%s",class_getName([string classForCoder]));
NSLog(#"%s",class_getName([string classForKeyedArchiver]));
Now, this won't work in all cases. For example, trying to get the class of NSConstantString, in this manner will output NSString. If you require checking the class name as a string in this way, you probably should reconsider your approach to solving the problem.
NSString is a so-called "class cluster". That means that the init methods will return
an instance of some subclass (such as __NSCFConstantString or __NSCFString).
You will never get an instance with the class equal to NSString.
If your intention is to check whether an object is a NSString or not then
use isKindOfClass:
if ([s isKindOfClass:[NSString class]]) {
// this is a string …
}
Other examples of class clusters are NSNumber, NSDictionary, NSArray
and their mutable variants.
NSLog(#"%#", NSStringFromClass([s class]));
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.
Is it possible to get all the keys from a specific NSDictionary as a seperate NSArray?
Just use
NSArray*keys=[dict allKeys];
In general, if you wonder if a specific class has a specific method, look up Apple's own documentation. In this case, see NSDictionary class reference. Go through all the methods. You'll discover many useful methods that way.
Yes it's possible. Use allKeys method:
NSDictionary *yourDictionary;
NSArray * yourKeys
yourKeys = [yourDictionary allKeys];
And if you want to get all keys and values, here's what you do:
for (NSString *key in dictionary) {
id value = dictionary[key];
NSLog(#"Value: %# for key: %#", value, key);
}
I need to create a dictionary/hashmap where the
Keys are enums
Values are some subclass of NSObject
NSDictionary won't work here (enums don't conform to NSCopying).
I could perhaps use a CFDictionaryRef here, but I'd like to know if is there any other way to achieve this.
Since enums are integers, you can wrap the enum in an NSNumber. When you add/retreive something to/from the map, you pass the enum to the NSNumber constructor...
Assuming you've got an enum like...
enum ETest {
FOO, BAR
};
You can use it in an NSDictionary like this...
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: #"Foo!" forKey:[NSNumber numberWithInt: FOO]];
NSLog(#"getting value for FOO -> %#",
[dict objectForKey: [NSNumber numberWithInt: FOO]]);
[dict release];
With VoidPointer's suggestion, it may be better to use NSValue for those times when enums turn out not to be integers (such as when -fshort-enums is in play, which should be never as you'd probably break compatibility with Foundation).
NSValue *value = [NSValue value: &myEnum withObjCType: #encode(enum ETest)];
That's not going to add much here but gives you the general "I want to use <name of non-ObjC type> in a collection class" technique.
Notice that with modern compilers you can tell enums to use a fixed underlying type. This means you can control what storage is used for the enum, but as the above solution is general it still applies even when you know this.
Further extending on the suggestion from Graham Lee...
You could use an objective-c category in order to add a method to NSMutableDictionary that allows you to add a value with a key of your non NSObject type. This keeps your code free from the wrapping/unwrapping syntax.
Again, assuming
enum ETest { FOO, BAR };
First, we're adding a convince constructor to NSValue:
#interface NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest;
#end
#implementation NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest
{
return [NSValue value: &etest withObjCType: #encode(enum ETest)];
}
#end
Next we'll add 'enum ETest' support to NSMutableDictionary
#interface NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key;
-(id) objectForETest:(enum ETest)key;
#end
#implementation NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key
{
[self setObject: anObject forKey:[NSValue valueWithETest:key]];
}
-(id) objectForETest:(enum ETest)key
{
return [self objectForKey:[NSValue valueWithETest:key]];
}
#end
The original Example can thus be transformed to
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: #"Bar!" forETest:BAR];
NSLog(#"getting value Bar -> %#", [dict objectForETest: BAR]);
[dict release];
Depending on how much you use your enum to access the dictionary this may ease readability of your code quite a bit.
enums don't conform to NSCopying
This is an understatement; enums do not "conform" to anything as they are not objects; they are primitive C values which are interchangeable with integers. That's the real reason why they can't be used as keys. The keys and values of NSDictionary need to be objects. But since enums are integers, you can just wrap them into NSNumber objects. This is probably the simplest option.
Another option, if the enums are contiguous from 0 up to some number (i.e. you didn't set any values manually), is that you can use an NSArray where the index represents the key enum's value. (Any "missing" entries would have to be filled with NSNull.)
The category approach has its own uses, but the newer boxed expressions (e.g. #(FOO)) should take care of type conversion for you. It works very transparently by explicitly boxing the enum when using it as a key.
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name, related stuff and also check if the respective object is NSString or not. How should I go about it?
I was trying something like the following. It wasn't working of course.
for(NSString *string in array){
NSLog(#"Name of the class : %#", [NSString stringWithCString:class_getName(Class id)];
If you're on Mac OS X, you can use [object className], it returns an NSString
for(id obj in array) NSLog(#"Name of the class: %#", [obj className]);
To check if it's a NSString, you should use something like this:
for(id obj in array) {
if ([obj isKindofClass:[NSString class]]) {
// do something
}
}
for(id object in array){
NSLog(#"Name of the class: %#", [object className]);
NSLog(#"Object is a string: %d", [object isKindOfClass:[NSString class]]);
}
Take a look at the NSObject class and protocol for other interesting methods.
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name & related stuff & also check if the respective object is NSString or not.
Hold up. Why do have an array of different typed objects in the first place? Could you redo your design to avoid getting into that situation?
As others have said, -isKindOfClass: works. One downside is it generally leads to brittle code. Here your loop needs to know about all the classes that could be in the array. Sometimes this is the best you can do though.
Designs that use -respondsToSelector: tend to be a little more robust. Here your loop would need to know about the behaviors it depends on of classes in the array.