Is there a way to grab a totally random key in the NSDictionary?
NSString *key = [enumeratorForKeysInDictionary nextObject];
I have this code which iterates over the dictionary in a non-random way.
Should I add all the keys to an NSSet and then pull randomly from there?
Is there a more efficient way to do this?
See this:
NSArray *array = [dictionary allKeys];
int random = arc4random()%[array count];
NSString *key = [array objectAtIndex:random];
NSArray* allKeys = [dictionary allKeys];
id randomKey = allKeys[arc4random_uniform([allKeys count])];
id randomObject = dictionary[randomKey];
You can also try something like this:
NSArray* allKeys = [myDictionary allKeys];
Then you can c method rand() to get a random index in the above NSArray to get the random key.
Same idea here, using a random index into keys, but a few improvements: (1) a dictionary category, (2) a block enumerator, like the native, (3) most important - to enumerate randomly, we must eliminate keys already visited. This will visit each key randomly, exactly once (unless the caller sets stop=YES in the block):
//
// NSDictionary+RandBlockEnum.h
//
#import <Foundation/Foundation.h>
#interface NSDictionary (RandBlockEnum)
- (void)enumerateKeysAndObjectsRandomlyUsingBlock:(void (^)(id, id, BOOL *))block;
#end
//
// NSDictionary+RandBlockEnum.m
//
#import "NSDictionary+RandBlockEnum.h"
#implementation NSDictionary (RandBlockEnum)
- (void)enumerateKeysAndObjectsRandomlyUsingBlock:(void (^)(id, id, BOOL *))block {
NSMutableArray *keys = [NSMutableArray arrayWithArray:[self allKeys]];
BOOL stop = NO;;
while (keys.count && !stop) {
id randomKey = keys[arc4random_uniform(keys.count)];
block(randomKey, self[randomKey], &stop);
[keys removeObject:randomKey];
}
}
#end
Call it like this:
#import "NSDictionary+RandBlockEnum.h"
NSDictionary *dict = #{ #"k1" : #"o1", #"k2" : #"o2", #"k3" : #"o3" };
[dict enumerateKeysAndObjectsRandomlyUsingBlock:^(id key, id object, BOOL *stop) {
NSLog(#"%#, %#", key, object);
}];
Modern, one-line version of #BestCoder's answer:
Objective-C
dictionary.allKeys[arc4random_uniform(dictionary.allKeys.count)]
Swift
Array(dictionary.keys)[Int(arc4random_uniform(UInt32(dictionary.keys.count)))]
You can create a set and then grab any object from the set.
NSSet *set = [NSSet setWithArray:[myDictionary allKeys]];
NSString *randomKey = [set anyObject];
SWIFT 4
you have a dictionary
let ads = _segueDataFromHomeToGuessMatch_NSType[Constants.Matches.ads] as! NSDictionary
get a random key value like this
let rd_ad_key = ads.allKeys[Int(arc4random_uniform(UInt32(ads.count)))] as! String
get value of the random key like this
let rd_ad_value = ads[rd_ad_key] as! String
Related
I have a NSMutableDictionary and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString objects
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:#{
#"key1" : #"value1",
#"key2" : #"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(#"%#", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
unique values (as they will become keys)
values conform to NSCopying (same as above)
no value is equal to any key (otherwise colliding names will be lost in the process)
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
Transverses the whole hierarchy of a collection, swapping all the keys
It's fast since it uses recursion and fast enumeration
Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
#end
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:#protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
#end
and...
#interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
#end
#implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
#end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = #{#"1" : #"a",
#"2" : #[ #{ #"5" : #"b" } ],
#"3" : #{#"6" : #"c"},
#"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>" represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption:
No key = value;
Complexity:
No extra space required. Will loop through once and replace all key value pairs.
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];
I have an NSMutableArray that looks like this
{
"#active" = false;
"#name" = NAME1;
},
{
"#active" = false;
"#name" = NAME2;
}
Is there a way to convert this to an NSDictionary and then use objectForKey to get an array of the name objects? How else can I get these objects?
There is a even shorter form then this proposed by Hubert
NSArray *allNames = [array valueForKey:#"name"];
valueForKey: on NSArray returns a new array by sending valueForKey:givenKey to all it elements.
From the docs:
valueForKey:
Returns an array containing the results of invoking
valueForKey: using key on each of the array's objects.
- (id)valueForKey:(NSString *)key
Parameters
key The key to retrieve.
Return Value
The value of the retrieved key.
Discussion
The returned array contains NSNull elements for each object that returns nil.
Example:
NSArray *array = #[#{ #"active": #NO,#"name": #"Alice"},
#{ #"active": #NO,#"name": #"Bob"}];
NSLog(#"%#\n%#", array, [array valueForKey:#"name"]);
result:
(
{
active = 0;
name = Alice;
},
{
active = 0;
name = Bob;
}
)
(
Alice,
Bob
)
If you want to convert NSMutableArray to corresponding NSDictionary, just simply use mutableCopy
NSMutableArray *phone_list; //your Array
NSDictionary *dictionary = [[NSDictionary alloc] init];
dictionary = [phone_list mutableCopy];
This is an Array of Dictionary objects, so to get the values you would:
[[myArray objectAtIndex:0]valueForKey:#"name"]; //Replace index with the index you want and/or the key.
This is example one of the exmple get the emplyee list NSMutableArray and create NSMutableDictionary.......
NSMutableArray *emloyees = [[NSMutableArray alloc]initWithObjects:#"saman",#"Ruchira",#"Rukshan",#"ishan",#"Harsha",#"Ghihan",#"Lakmali",#"Dasuni", nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSString *word in emloyees) {
NSString *firstLetter = [[word substringToIndex:1] uppercaseString];
letterList = [dict objectForKey:firstLetter];
if (!letterList) {
letterList = [NSMutableArray array];
[dict setObject:letterList forKey:firstLetter];
}
[letterList addObject:word];
} NSLog(#"dic %#",dict);
yes you can
see this example:
NSDictionary *responseDictionary = [[request responseString] JSONValue];
NSMutableArray *dict = [responseDictionary objectForKey:#"data"];
NSDictionary *entry = [dict objectAtIndex:0];
NSString *num = [entry objectForKey:#"num"];
NSString *name = [entry objectForKey:#"name"];
NSString *score = [entry objectForKey:#"score"];
im sorry if i can't elaborate much because i am also working on something
but i hope that can help you. :)
No, guys.... the problem is that you are stepping on the KeyValue Mechanism in cocoa.
KeyValueCoding specifies that the #count symbol can be used in a keyPath....
myArray.#count
SOOOOOO.... just switch to the ObjectForKey and your ok!
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"theValue", #"#name", nil];
id kvoReturnedObject = [myDictionary valueForKey:#"#name"]; //WON'T WORK, the # symbol is special in the valueForKey
id dictionaryReturnedObject = [myDictionary objectForKey:#"#name"];
NSLog(#"object = %#", dictionaryReturnedObject);
for(Attribute* attribute in appDelegate.attributeArray) {
attribute = [appDelegate.attributeArray objectAtIndex:z];
attri = attribute.zName;
int y = 0;
for(Row* r in appDelegate.elementsArray) {
r = [appDelegate.elementsArray objectAtIndex:y];
NSString *ele = r.language;
if([attri isEqualToString:ele]) {
NSLog(#"=================+++++++++++++++++%# %#",attri, r.user);
[aaa insertObject:r atIndex:y]; //here i am adding the value to array
[dict setObject:aaa forKey:attri]; //here i am adding the array to dictionary
}
y++;
}
z++;
NSLog(#"============$$$$$$$$$$$$$$$$$$$$$$$++++++++++ %#",dict);
}
key in one array and the value in the another array and the value array is in object format.
I need to store the multi object for the single key. The attributeArray has the key value and the elementsArray has the object. For example the attributeArray might have the values
" English, French, German..."
and the elementsArray might have the object value
"<Row: 0x4b29d40>, <Row: 0x4b497a0>, <Row: 0x4e38940>, <Row: 0x4b2a070>, <Row: 0x4b29ab0>, <Row: 0x4b178a0> "
In the first value I need to store the two object and for second key I need to store 3 objects and for the third key in need to store last two objects in the dictionary.
For super-simplification you can use the following code:
NSArray *keys = ...;
NSArray *values = ...;
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects: values forKeys: keys];
Hope, this helps.
UPDATE:
to store multiple values for single key in the dictionary, just use NSArray / NSMutableArray as your object:
NSArray *keys = ...;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for( id theKey in keys)
{
NSMutableArray *item = [NSMutableArray array];
[item addObject: ...];
[item addObject: ...];
...
[dict setObject: item forKey: theKey];
}
If you don't know all the values for the key from the beginning and need to add them one by one, you can use the following approach:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for( /*some cycling condition here */)
{
id keyToUse = ...;
id valueToAdd = ...;
id foundArray = [dict objectForKey: keyToUse];
if ( nil == foundArray )
{
foundArray = [NSMutableArray array];
[dict setObject: foundArray forKey: keyToUse];
}
[foundArray addObject: valueToAdd];
}
To me it looks you are settings an array (aaa) with string (attri) as a key.
To set an array as a key for another array as an object. You can do this with the following appraoch.
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
NSArray *valueArray = [NSArray arrayWithObjects:#"v1", #"v2", nil];
NSArray *keyArray = [NSArray arrayWithObjects:#"k1",#"k2", nil];
[myDictionary setObject:valueArray forKey:keyArray];
NSLog(#"myDictionary: %#", myDictionary);
But you should review your code and provide a more brief explanation that what do you want to achieve and how are you taking an approach for this.
regards,
Arslan
I have and array of many strings.
I wan't to sort them into a dictionary, so all strings starting the same letter go into one array and then the array becomes the value for a key; the key would be the letter with which all the words in it's value's array begin.
Example
Key = "A" >> Value = "array = apple, animal, alphabet, abc ..."
Key = "B" >> Value = "array = bat, ball, banana ..."
How can I do that?
Thanks a lot in advance!
NSArray *list = [NSArray arrayWithObjects:#"apple, animal, bat, ball", nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSString *word in list) {
NSString *firstLetter = [[word substringToIndex:1] uppercaseString];
NSMutableArray *letterList = [dict objectForKey:firstLetter];
if (!letterList) {
letterList = [NSMutableArray array];
[dict setObject:letterList forKey:firstLetter];
}
[letterList addObject:word];
}
NSLog(#"%#", dict);
You can achieve what you want through the following steps:
Create an empty but mutable dictionary.
Get the first character.
If a key for that character does not exist, create it.
Add the word to the value of the key (should be an NSMutableArray).
Repeat step #2 for all keys.
Here is the Objective-C code for these steps. Note that I am assuming that you want the keys to be case insensitive.
// create our dummy dataset
NSArray * wordArray = [NSArray arrayWithObjects:#"Apple",
#"Pickle", #"Monkey", #"Taco",
#"arsenal", #"punch", #"twitch",
#"mushy", nil];
// setup a dictionary
NSMutableDictionary * wordDictionary = [[NSMutableDictionary alloc] init];
for (NSString * word in wordArray) {
// remove uppercaseString if you wish to keys case sensitive.
NSString * letter = [[word substringWithRange:NSMakeRange(0, 1)] uppercaseString];
NSMutableArray * array = [wordDictionary objectForKey:letter];
if (!array) {
// the key doesn't exist, so we will create it.
[wordDictionary setObject:(array = [NSMutableArray array]) forKey:letter];
}
[array addObject:word];
}
NSLog(#"Word dictionary: %#", wordDictionary);
Take a look at this topic, they solves almost the same problem as you — filtering NSArray into a new NSArray in objective-c Let me know if it does not help so I will write for you one more code sample.
Use this to sort the contents of array in alphabetical order, further you design to the requirement
[keywordListArr sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I just wrote this sample. It looks simple and does what you need.
NSArray *names = [NSArray arrayWithObjects:#"Anna", #"Antony", #"Jack", #"John", #"Nikita", #"Mark", #"Matthew", nil];
NSString *alphabet = #"ABCDEFGHIJKLMNOPQRSTUWXYZ";
NSMutableDictionary *sortedNames = [NSMutableDictionary dictionary];
for(int characterIndex = 0; characterIndex < 25; characterIndex++) {
NSString *alphabetCharacter = [alphabet substringWithRange:NSMakeRange(characterIndex, 1)];
NSArray *filteredNames = [names filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF BEGINSWITH[C] %#", alphabetCharacter]];
[sortedNames setObject:filteredNames forKey:alphabetCharacter];
}
//Just for testing purposes let's take a look into our sorted data
for(NSString *key in sortedNames) {
for(NSString *value in [sortedNames valueForKey:key]) {
NSLog(#"%#:%#", key, value);
}
}
I'm developing an iPhone application.
I use a NSDictionary to store city's names as key, and population as value. I want to search the keys using lowercase.
I've using this:
NSDictionary *dict;
[dict objectForKey:[[city stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString]];
But, it doesn't work.
I know, I can do a for, convert keys to lowercase and compare with city.
Is there any other way to do that? Maybe, with a NSDictionary method.
UPDATE The NSDictionary is loaded from a property list.
Thank you.
I use this method in an NSDictionary category.
#implementation NSDictionary (MyCategory)
- (NSDictionary *)dictionaryWithLowercaseKeys {
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:0];
NSString *key;
for (key in self) {
[result setObject:[self objectForKey:key] forKey:[key lowercaseString]];
}
return result;
}
#end
Although I'm still not clear on what you want, this loop with search for keys case insensitively. Getting the value of that key is then trivial.
for key in dict
{
if ([key caseInsensitiveCompare: #"Whatever"] == NSOrderedSame)
NSLog(#"They are equal.");
}
I'd say create a second dictionary. After you load from the property set, loop through that dictionary and insert objects into the second dictionary. Convert the keys to lowercase as you go. Then release the first dictionary.
The above answers only work for a flat dictionary without any nesting whatsoever. I created a category that creates a copy of an existing NSDictionary with every key converted to lower case.
The header: NSDictionary+LowercaseKeys.h
#import <Foundation/Foundation.h>
#interface NSDictionary (LowercaseKeys)
/*
Recursive algorithm to find all nested dictionary keys and create an NSMutableDictionary copy with all keys converted to lowercase.
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
+ (NSMutableDictionary *)dictionaryWithLowercaseKeysFromDictionary:(NSDictionary *)dictionary;
/*
Convienience method to create a new lowercase dictionary object an existing NSDictionary instance
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
- (NSMutableDictionary *)dictionaryWithLowercaseKeys;
#end
The Implementation: NSDictionary+LowercaseKeys.m
#import "NSDictionary+LowercaseKeys.h"
#implementation NSDictionary (LowercaseKeys)
/*
Recursive algorithm to find all nested dictionary keys and create an NSMutableDictionary copy with all keys converted to lowercase
Returns an NSMutableDictionary with all keys and nested keys converted to lowercase.
*/
+ (NSMutableDictionary *)dictionaryWithLowercaseKeysFromDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[dictionary count]];
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
// There are 3 types of objects to consider, NSDictionary, NSArray and everything else
id resultObj;
if ([obj isKindOfClass:NSDictionary.class])
{
// Recursively dig deeper into this nested dictionary
resultObj = [NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:obj];
}
else if ([obj isKindOfClass:NSArray.class])
{
/*
Iterate over this nested NSArray. Recursively convert any NSDictionary objects to the lowercase version.
If the array contains another array then continue to recursively dig deeper.
*/
resultObj = [NSMutableArray arrayWithCapacity:[obj count]];
for (id arrayObj in obj)
{
if ([arrayObj isKindOfClass:NSDictionary.class])
[resultObj addObject:[NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:arrayObj]];
else if ([arrayObj isKindOfClass:NSArray.class])
[resultObj addObject:[NSMutableDictionary arrayWithLowercaseKeysForDictionaryArray:arrayObj]];
else
[resultObj addObject:arrayObj];
}
}
else
{
// The object is not an NSDictionary or NSArray so keep the object as is
resultObj = obj;
}
// The result object has been converted and can be added to the dictionary. Note this object may be nested inside a larger dictionary.
[resultDict setObject:resultObj forKey:[key lowercaseString]];
}];
return resultDict;
}
/*
Convienience method to create a new dictionary object with all lowercase keys from an existing instance
*/
- (NSMutableDictionary *)dictionaryWithLowercaseKeys
{
return [NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:self];
}
#pragma mark - Private helpers
/*
Convert NSDictionary keys to lower case when embedded in an NSArray
*/
+ (NSMutableArray *)arrayWithLowercaseKeysForDictionaryArray:(NSArray *)dictionaryArray
{
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[dictionaryArray count]];
for (id eachObj in dictionaryArray)
{
if ([eachObj isKindOfClass:NSDictionary.class])
[resultArray addObject:[NSMutableDictionary dictionaryWithLowercaseKeysFromDictionary:eachObj]];
else if ([eachObj isKindOfClass:NSArray.class])
[resultArray addObject:[NSMutableDictionary arrayWithLowercaseKeysForDictionaryArray:eachObj]];
}
return resultArray;
}
#end