I have 100 key and value in nsmutabledictornary and i want to check that any value have null or not. Do you have any short function or technique?
I don't want to multiple line code like check every key and value. Your answer would be appreciated.
This code will give you the set of keys which have (non)null values. You can't store actual nil values in a dictionary, so [NSNull null] is assumed. The predicate is trivially alterable to any other condition.
NSDictionary *d = #{ #"a" : #"1", #"b" : [NSNull null] };
NSSet *nullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]];
}];
NSSet *nonnullKeys = [d keysOfEntriesPassingTest:^BOOL(NSString *key, id obj, BOOL *stop) {
return [d[key] isKindOfClass:[NSNull class]] == NO;
}];
From here, you can use the keys to generate a corresponding dictionary, if needed.
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([nonnullKeys contains:key]) {
nonNullDict[key] = obj;
}
}];
If you don't need a separate list of keys, and just need the filtered dictionary, skip the first step and modify the second part to read as follows:
NSMutableDictionary *nonNullDict = [NSMutableDictionary dictionary];
[d enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSNull null]] == NO) {
nonNullDict[key] = obj;
}
}];
Write category on NSDictionary it will provide you null free dictionary. Here is the category I have written for myself.
code for .h file (interface)
#import <Foundation/Foundation.h>
#interface NSDictionary (CheckNull)
{
}
- (NSDictionary *)nullFreeDictionary;
#end
Code for .m file. (implementation)
#import "NSDictionary+CheckNull.h"
#implementation NSDictionary (CheckNull)
- (NSDictionary *) nullFreeDictionary
{
NSMutableDictionary *tempDictionary = [self mutableCopy];
for (NSString *key in tempDictionary.allKeys) {
NSString *value = [tempDictionary valueForKey:key];
if ([value isKindOfClass:[NSString class]]) {
if (value == (id)[NSNull null] || value == nil || value.length == 0) {
[tempDictionary setValue:#"" forKey:key];
}
}
}
return tempDictionary;
}
Call null free method on your dictionary using above category.
NSDictionary *dict = [dict nullFreeDictionary];
//To remove NULL from Dictionary
-(NSMutableDictionary *)removeNullFromDictionary : (NSMutableDictionary *)dict
{
// if (![dict isKindOfClass:[NSMutableDictionary class]])
// {
// }
dict = [[NSMutableDictionary alloc] initWithDictionary:dict];
for (NSString * key in [dict allKeys])
{
if ([dict[key] isKindOfClass:[NSNull class]])
{
[dict setValue:#"" forKey:key];
}
else if ([dict[key] isKindOfClass:[NSMutableDictionary class]]||[dict[key] isKindOfClass:[NSDictionary class]])
{
dict[key] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:dict[key]]];
}
else if ([dict[key] isKindOfClass:[NSMutableArray class]]||[dict[key] isKindOfClass:[NSArray class]])
{
dict[key] = [self removeNullFromArray:[NSMutableArray arrayWithArray:dict[key]]];
}
}
return dict;
}
//To remove NULL from Array
-(NSMutableArray *)removeNullFromArray : (NSMutableArray *)arr
{
// if (![arr respondsToSelector:#selector(addObject:)])
// {
// arr = [[NSMutableArray alloc] initWithArray:arr];
// }
arr = [[NSMutableArray alloc] initWithArray:arr];
for (int cnt = 0; cnt<[arr count]; cnt++)
{
if ([arr[cnt] isKindOfClass:[NSNull class]])
{
arr[cnt] = #"";
}
else if ([arr[cnt] isKindOfClass:[NSMutableDictionary class]]||[arr[cnt] isKindOfClass:[NSDictionary class]])
{
arr[cnt] = [self removeNullFromDictionary:[NSMutableDictionary dictionaryWithDictionary:arr[cnt]]];
}
else if ([arr[cnt] isKindOfClass:[NSMutableArray class]]||[arr[cnt] isKindOfClass:[NSArray class]])
{
arr[cnt] = [self removeNullFromArray:[NSMutableArray arrayWithArray:arr[cnt]]];
}
}
return arr;
}
Related
I want to change the Dictionary's all value to String, how to do with it?
Such as:
{ #"a":"a",
#"b":2,
#"c":{
#"c1":3,
#"c2":4
}
}
I want convert to :
{ #"a":"a",
#"b":"2",
#"c":{
#"c1":"3",
#"c2":"4"
}
}
How to do with it? I think all the day.
If I use below method to traverse the dictionary values:
NSArray *valueList = [dictionary allValues];
for (NSString * value in valueList) {
// change the value to String
}
If the value is a dictionary, how about it?
So, someone can help with that?
You could do this with a recursive method, it changes all NSNumber values to NSString and calls itself for nested dictionaries. Since a dictionary cannot be mutated while being enumerated a new dictionary is created and populated:
- (void)changeValuesOf:(NSDictionary *)dictionary result:(NSMutableDictionary *)result
{
for (NSString *key in dictionary) {
id value = dictionary[key];
if ([value isKindOfClass: [NSDictionary class]]) {
NSMutableDictionary * subDict = [NSMutableDictionary dictionary];
result[key] = subDict;
[self changeValuesOf:value result:subDict];
} else if ([value isKindOfClass: [NSNumber class]]) {
result[key] = [NSString stringWithFormat:#"%#", value];
} else {
result[key] = value;
}
}
}
NSDictionary *dictionary = #{#"a": #"a", # "b":#2, #"c": #{#"c1": #3, #"c2":#4 }};
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[self changeValuesOf:dictionary result:result];
NSLog(#"%#", result);
You can create category for a dictionary and add method some like stringValueForKey:.
The realisation can be something like this:
- (NSString)stringValueForKey:(NSString*)key
{
id value = self[key];
if( [value respondsToSelector:#selector(stringValue)])
return [value performSelector:#selector(stringValue)]
return nil;
}
I have a Array of custom objects with object having following properties optionID,OptionText. I want to get comma separated string for the optionID property. What would be the best approach to do this in iOS SDK.
for example NSString CommaSeperted = #"1,3,5" etc.
Category to NSArray:
#implementation NSArray(CustomAdditions)
- (NSString *)commaSeparatedStringWithSelector:(SEL)aSelector
{
NSMutableArray *objects = [NSMutableArray array];
for (id obj in self)
{
if ([obj respondsToSelector:aSelector]) {
IMP method = [obj methodForSelector:aSelector];
id (*func)(id, SEL) = (void *)method;
id customObj = func(obj, aSelector);
if (customObj && [customObj isKindOfClass:[NSString class]]) {
[objects addObject:customObj];
}
}
}
return [objects componentsJoinedByString:#","];
}
#end
Example:
#implementation NSDictionary(Test)
- (NSString*)optionID
{
return [self objectForKey:#"optionID"];
}
- (NSString*)OptionText
{
return [self objectForKey:#"OptionText"];
}
#end
NSArray *customObjects = #[#{#"optionID": #"id1", #"OptionText": #"text1" }, #{#"optionID" : #"id2", #"OptionText": #"text2"}];//List of Your custom objects
NSString *commaSeparatedOptionIDs = [customObjects commaSeparatedStringWithSelector:NSSelectorFromString(#"optionID")];
NSString *commaSeparatedOptionTexts = [customObjects commaSeparatedStringWithSelector:NSSelectorFromString(#"OptionText")];
Try this
NSString *commaSeparatedStringOfID = #"";
for (CustomClass *object in yourArray){
commaSeparatedStringOfID = [commaSeparatedStringOfID stringByAppendingString:[NSString stringWithFormat:#"%#,"]];
}
// removing last comma
commaSeparatedStringOfID = [commaSeparatedStringOfID substringToIndex:[commaSeparatedStringOfID length]-1];
commaSeparatedStringOfID will be your required string.
+ (NSDictionary *)mc_inboundMapping {
static NSMutableDictionary *mappingForClassName = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mappingForClassName = [NSMutableDictionary dictionary];
});
#synchronized(mappingForClassName) {
NSDictionary *mapping = mappingForClassName[[self className]];//<----EXC BAD ACCESS here
if (!mapping) {
SEL selector = NSSelectorFromString(#"JSONInboundMappingDictionary");
if ([self respondsToSelector:selector]) {
mapping = MCValueFromInvocation(self, selector);
}
else {
mapping = [self mc_defaultInboundMapping];
}
mappingForClassName[[self className]] = mapping;
}
return mapping;
}
I'm using Realm+JSON with Realm. And got a problem with Realm+JSON.
When I attempt to call createOrUpdateInRealm:withJSONArray: multiple time in loop for multiple RLMObjects, the first RLMObject works OK, but the second RLMObject(no matter what class is) fails with EXC BAD ACCESS code1on static variable.
I think autorelease pool deallocate pointing NSMutableDictionary.Anybody got this issue? I'm using XCode 6.3.2 and Realm 0.96.3.
+ (NSArray *)createOrUpdateInRealm:(RLMRealm *)realm withJSONArray:(NSArray *)array {
NSInteger count = array.count;
NSMutableArray *result = [NSMutableArray array];
for (NSInteger index=0; index*kCreateBatchSize<count; index++) {
NSInteger size = MIN(kCreateBatchSize, count-index*kCreateBatchSize);
#autoreleasepool
{
for (NSInteger subIndex=0; subIndex<size; subIndex++) {
NSDictionary *dictionary = array[index*kCreateBatchSize+subIndex];
id object = [self createOrUpdateInRealm:realm withJSONDictionary:dictionary];
[result addObject:object];
}
}
}
return [result copy];
}
calling here...
[realm beginWriteTransaction];
if ([collection isKindOfClass:[NSDictionary class]])
{
for (NSString *key in [collection allKeys])
{
NSLog(#"Set Data == %#:%#", key, collection[key]);
id jsData = [collection[key] objectForKey:#"data"];
if ([key isEqualToString:STATIC_ERROR_MSGS])
{
[SErrorMsgs createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_EPISODE])
{
[SEpisode createOrUpdateInRealm:realm withJSONArray:jsData];
}
else if ([key isEqualToString:STATIC_INGAME_TUTORIAL])
{
[SIngameTutorial createOrUpdateInRealm:realm withJSONArray:jsData];
} //more cases.........edited
I have a problem to communicate with a server. The webserver expects all parameters in the JSON object to be a string. So every number and every boolean in every container needs to be a string.
For my example I have a NSDictionary full of key values (values are all kinds of types - numbers, arrays etc.). For example:
{
"AnExampleNumber":7e062fa,
"AnExampleBoolean":0,
"AnExampleArrayOfNumber":[17,4,8]
}
Has to become:
{
"AnExampleNumber":"7e062fa",
"AnExampleBoolean":"0",
"AnExampleArrayOfNumber":["17","4","8"]
}
I tried the standard NSJSONSerializer but it doesn't give me any option to do what I need. I then tried to transform everything in the dictionary manually to be a string but that seems to be overhead. Does anyone have hint for me? Maybe a serializer that does just that or a function to convert any objects in a container to strings?
This is one way you could do it. It's non-optimized and has no error handling. It only supports the kinds of objects that NSJSONSerializer supports.
#import <Foundation/Foundation.h>
#interface NSObject(SPWKStringify)
- (id)spwk_stringify;
#end
#implementation NSObject(SPWKStringify)
- (id)spwk_stringify
{
if ([self isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)self;
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
for (NSString *key in [dict allKeys]) {
newDict[key] = [dict[key] spwk_stringify];
}
return newDict;
} else if ([self isKindOfClass:[NSArray class]]) {
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (id value in ((NSArray *)self)) {
[newArray addObject:[value spwk_stringify]];
}
return newArray;
} else if (self == [NSNull null]) {
return #"null"; // representing null as a string doesn't make much sense
} else if ([self isKindOfClass:[NSString class]]) {
return self;
} else if ([self isKindOfClass:[NSNumber class]]) {
return [((NSNumber *)self) stringValue];
}
return nil;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDictionary *dict = #{
#"AnExampleNumber": #1234567,
#"AnExampleBoolean": #NO,
#"AnExampleNull": [NSNull null],
#"AnExampleArrayOfNumber": #[#17, #4, #8],
#"AnExampleDictionary": #{#"innerKey": #[#55, #{#"anotherDict": #[#"foo", #[#1, #2, #"three"]]}]}
};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[dict spwk_stringify] options:NSJSONWritingPrettyPrinted error:NULL];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"result: %#", jsonString);
}
}
The output will be:
result: {
"AnExampleNumber" : "1234567",
"AnExampleNull" : "null",
"AnExampleDictionary" : {
"innerKey" : [
"55",
{
"anotherDict" : [
"foo",
[
"1",
"2",
"three"
]
]
}
]
},
"AnExampleBoolean" : "0",
"AnExampleArrayOfNumber" : [
"17",
"4",
"8"
]
}
Note: Please keep in mind that turning [NSNull null] into a string doesn't make any sense and might actually be misleading and dangerous.
Enjoy.
(I assume you mean NSJSONSerializer, not NSSerializer.)
I doubt you'll find a pre-rolled solution to this. It's not a general problem. As you note, this is incorrect JSON, so JSON serializers shouldn't do it.
The best solution IMO is just write the code to transform your NSDictionary into another NSDictionary that is in the form you want. If you really want to make it a generic solution, I suspect that a custom NSDictionary walker with isKindOfClass: is your best bet. Something like this should work:
NSDictionary *myStringDictForDict(NSDictionary *dict); // forward decl if needed
NSArray *myStringArrayForArray(NSArray *array) {
NSMutableArray *result = [NSMutableArray new];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
[result addObject:myStringArrayForArray(obj)];
} else if ([obj isKindOfClass:[NSDictionary class]]) {
[result addObject:myStringDictForDict(obj)];
} else {
[result addObject:[obj description]];
}
}];
return result;
}
NSDictionary *myStringDictForDict(NSDictionary *dict) {
NSMutableDictionary *result = [NSMutableDictionary new];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
result[key] = myStringArrayForArray(obj);
} else if ([obj isKindOfClass:[NSDictionary class]]) {
result[key] = myStringDictForDict(obj);
} else {
result[key] = [obj description];
}
}];
return result;
}
I have an array with following elements in ViewDidLoad method
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
I have another UITextField for searching the elements .So once i type some thing in UITextField i want to check whether that string is present in "inputArray" or not.If it is not matching with elements in inputArray then remove the corresponding elements from inputArray .
for (NSString* item in inputArray)
{
if ([item rangeOfString:s].location == NSNotFound)
{
[inputArray removeObjectIdenticalTo:item];//--> Shows Exception
NSLog(#"Contains :%#",containsAnother);
}
}
but this code shows exception , something related to "removeobject:"
Exception :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument'
*** First throw call stack:
`
In fast enumeration you can NOT modify the collection.
The enumerator object becomes constant and immutable.
If you want to do updation on the array
You should like this :
NSMutableArray *inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
NSString *s=#"bus";
for (int i=inputArray.count-1; i>-1; i--) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
}
}
EDIT:
The above works similar as this :
NSArray *array=[inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF CONTAINS[c] %#",s]];
You can use the following code
for (int i=0;i<[inputArray count]; i++) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
i--;
}
}
That needs to be an NSMutableArray. You can't modify an NSArray once created (except to start all over).
Change this:
inputArray = [NSArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
to this:
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
and also change the property to NSMutableArray also:
#property(nonatomic, strong) NSMutableArray *inputArray;
The s in your question is probably nil. So your are getting the exception.Please check that out.
Use following Code. (This Code is use for filter Array base on input string/text of UITextField )
Take Two NSMutableArray and add one array to another array in ViewDidLoad method such like,
self.listOfTemArray = [[NSMutableArray alloc] init]; // array no - 1
self.ItemOfMainArray = [[NSMutableArray alloc] initWithObjects:#"YorArrayList", nil]; // array no - 2
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray]; // add 2array to 1 array
And Write following delegate Method of UISearchBar
- (BOOL) textFieldDidChange:(UITextField *)textField
{
NSString *name = #"";
NSString *firstLetter = #"";
if (self.listOfTemArray.count > 0)
[self.listOfTemArray removeAllObjects];
if ([searchText length] > 0)
{
for (int i = 0; i < [self.ItemOfMainArray count] ; i = i+1)
{
name = [self.ItemOfMainArray objectAtIndex:i];
if (name.length >= searchText.length)
{
firstLetter = [name substringWithRange:NSMakeRange(0, [searchText length])];
//NSLog(#"%#",firstLetter);
if( [firstLetter caseInsensitiveCompare:searchText] == NSOrderedSame )
{
// strings are equal except for possibly case
[self.listOfTemArray addObject: [self.ItemOfMainArray objectAtIndex:i]];
NSLog(#"=========> %#",self.listOfTemArray);
}
}
}
}
else
{
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray ];
}
[self.tblView reloadData];
}
}
Output Show in your Console.
As others have said you can't mutate an array while it is being enumerated. The easiest way to do what you want and keep the convenience of fast enumeration is to copy the array.
for (NSString* item in [inputArray copy]) {
...
}
This is a clean solution that I like to use. You define a NSArray category to extend it and create a map method. This method creates a new NSArray based on what you return within your block:
#interface NSArray (BlockExtensions)
/*!
Invokes block once for each element of self, returning a new array containing the
values returned by the block.
*/
- (NSArray *)map:(id (^)(id obj))block;
#end
#implementation NSArray (BlockExtensions)
- (NSArray *)map:(id (^)(id obj))block
{
return [self mapWithOptions:0 usingBlock:^id(id obj, NSUInteger idx) {
return block(obj);
}];
}
- (NSArray *)mapWithOptions:(NSEnumerationOptions)options usingBlock:(id (^)(id obj, NSUInteger idx))block
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
[self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id newobj = block? block(obj, idx) : obj;
if (newobj)
[array addObject:newobj];
}];
return array;
}
#end
The block will be called once for every item in your original array, passing this object as a parameter:
NSArray *newArray = [inputArray map:^id(NSString *item) {
if ([item rangeOfString:s].location == NSNotFound) {
return item;
}
return nil;
}];
newArray will contain your filtered out items!
+1 to Anoop for pointing out that you can use filteredArrayUsingPredicate. Thus, if you wanted to create a new array based upon the matches in inputArray, you could also use something like:
NSArray *matchingArray = [inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Alternatively, given that inputArray is a NSMutableArray you can simply filter the array with this single line:
[inputArray filterUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Or, if you like blocks:
[inputArray filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject rangeOfString:s].location != NSNotFound);
}]];