I am trying to create a String from Array.But, there is condition to how it should be generated, as explained below.
NSArray *array=[NSArray arrayWithObjects:#"Hello",#"World",nil];
[array componentsJoinedByString:#","];
This will output: Hello,World.
But, if first Item is Empty,then is there way to receive the only second one.
Hello , #"" => Hello
#"" , World => World
Hello , World => Hello,World
Another way to do this is to grab a mutable copy of the array and just remove non valid objects. Something like this perhaps:
NSMutableArray *array = [[NSArray arrayWithObjects:#"",#"World",nil] mutableCopy];
[array removeObject:#""]; // Remove empty strings
[array removeObject:[NSNull null]]; // Or nulls maybe
NSLog(#"%#", [array componentsJoinedByString:#","]);
You cannot store nil values in NSArray*, so the answer is "no". You need to iterate the array yourself, keeping track of whether you need to add a comma or not.
NSMutableString *res = [NSMutableString string];
BOOL first = YES;
for(id item in array) {
if (id == [NSNull null]) continue;
// You can optionally check for item to be an empty string here
if (!first) {
[res appendString:#", "];
} else {
first = NO;
}
[res appendFormat:#"%#", item];
}
* nil values in NS collections are represented with NSNull objects.
Related
After a network call to the Instagram API, I get back a responseDictionary NSDictionary delegate with the following Key/Value structure:
{
data = (
{
bio = "Los Angeles/Orange County Realtor\U00ae \n\U6d1b\U6749\U77f6\U623f\U5730\U4ea7\U7ecf\U7eaa\U4eba\nCall/Text/WhatsApp: (310) 717-1321\nEmail: Jxxxcom\nWeChat (\U5fae\U4fe1): xx";
"full_name" = "xx yy (\U7530\U4f73\U6dfc) Rx Realty";
id = 25354408;
"profile_picture" = "http://scontent-a.cdninstagram.com/hphotos-xpa1/outbound-distillery/t0.0-20/OBPTH/profiles/profile_xxx_75sq_1391378894.jpg";
username = jxxi;
website = "http://www.Jxghty.com";
},
The profile_picture key often has an NSString value that contains anonymousUser (for the users who didn't set any profile pictures).
I am looking to remove those entries from my responseDictionary as follows:
//Create mutable copy of IG responseDictionary
NSMutableDictionary *dictCleanAvatars = [responseDictionary mutableCopy];
NSLog(#"Log dictCleanAvatars after mutableCopy IG response: %#", dictCleanAvatars);
NSArray *keys = [dictCleanAvatars allKeys]; //get all the keys
NSUInteger k2 = [dictCleanAvatars count];
NSLog(#"k2 in dictCleanAvatars before cleanup is: %lu", (unsigned long)k2);
for (int i = 0; i<k2; i++)
{
if ([[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] isKindOfClass:[NSString class]])
{
//if its an NSString - don't want an exception if its another type of object
NSLog(#"Yes, objectAtIndex:i us Kind ofClass NSString for i = %d", i);
if ([[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] rangeOfString:#"anonymousUser"].location != NSNotFound)
{
NSLog(#"Yes, anonymousUser identified in objectAtIndex:i for i = %d", i);
//if object has the key word im looking for
[dictCleanAvatars removeObjectForKey:[keys objectAtIndex:i]]; //remove the key
NSLog(#"That's dictCleanAvatars after loop %d: %#", i, dictCleanAvatars);
}
}
}
But this doesn't work.
Would value feedback from more experience iOS developers.
If you're trying to build an array that includes everything from the data key's array, but omitting those dictionaries for which profile_picture contains the string "AnonymousUser", you can use NSPredicate:
NSArray *dataArray = responseDictionary[#"data"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"not (profile_picture contains 'AnonymousUser')"];
NSArray *filteredArray = [dataArray filteredArrayUsingPredicate:predicate];
Or you can use predicateWithBlock:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject[#"profile_picture"] rangeOfString:#"AnonymousUser"].location == NSNotFound;
}];
BTW, if you already have a mutable array, you can also remove entries from it using filterUsingPredicate, using the above predicates:
NSMutableArray *mutableDataArray = [responseDictionary[#"data"] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"not (profile_picture contains 'AnonymousUser')"];
[mutableDataArray filterUsingPredicate:predicate];
If, on the other hand, you don't want to remove entire dictionaries from the array of dictionaries, but rather want to simply remove the occurrences of profile_picture for which "AnonymousUser" is present, you want to ensure that not only is the array mutable, but so are its constituent dictionaries.
The easiest way of doing this is to specify the NSJSONReadingMutableContainers option when parsing the JSON. Then you can just iterate through the NSMutableDictionary entries, removing the profile_picture entries with a profile_picture with "AnonymousUser" in them:
NSMutableDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
NSMutableArray *mutableDataArray = responseDictionary[#"data"];
for (NSMutableDictionary *dictionary in mutableDataArray) {
NSString *profilePicture = dictionary[#"profile_picture"];
if ([profilePicture rangeOfString:#"AnonymousUser"].location != NSNotFound) {
[dictionary removeObjectForKey:#"profile_picture"];
}
}
If, however, you can't specify the NSJSONReadingMutableContainers option when you parse the JSON and are stuck with a immutable collection, you need to make a mutable copy of it. Unfortunately, a simple mutableCopy of the array won't make the member dictionaries mutable themselves, but you can use a Core Foundation call to CFPropertyListCreateDeepCopy to make a mutable array with mutable entries, which you can then modify:
NSMutableArray *mutableDataArray = CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)responseDictionary[#"data"], kCFPropertyListMutableContainers));
Then you can use the above for loop, iterating through this array's dictionary entries, removing the offending profile_picture entries.
if [[dictCleanAvatars objectForKey:[keys objectAtIndex:i]] isEqualToString#"anonymousUser"] {
The problem is, suppose [dictCleanAvatars objectForKey:[keys objectAtIndex:i]] is not an NSString? You might want to check for that first.
If the only field you are looking at is profile_picture, I would go with a less generic approach which is much more readable and understandable
This code works for me
- (void)testExample
{
NSDictionary *dictionary = #{ #"data": #[ #{ #"bio": #"blah blah", #"profile_picture": #"some stuff anonymousUser other stuff" },
#{ #"bio": #"some other object", #"profile_picture": #"some other profile picture link" }] };
// dictionary is a mock of the data you provided
NSArray *data = [dictionary objectForKey:#"data"];
for (NSDictionary * avatarDict in data) {
NSMutableDictionary *mdict = [avatarDict mutableCopy];
id ppid = [mdict objectForKey:#"profile_picture"];
if ([ppid isKindOfClass:[NSString class]]) {
NSString *pp = (NSString *)ppid;
if ([pp rangeOfString:#"anonymousUser"].location != NSNotFound) {
[mdict removeObjectForKey:#"profile_picture"];
}
}
NSLog(#"altered dictionary: %#", mdict);
}
}
Output:
2014-08-13 10:53:36.727 test[11981:60b] altered dictionary: {
bio = "blah blah";
}
2014-08-13 10:53:36.728 test[11981:60b] altered dictionary: {
bio = "some other object";
"profile_picture" = "some other profile picture link";
}
I need a way to know if an array has the character "#" in one of its string objects. The following code obviously doesn't work because it checks if an object just has the # sign instead of checking if an object contains the # sign. For example, if the user has test#test.com my if statement won't detect it. I need to see if a user has an email or not. I tried researching on how to accomplish this on stackoverflow, but no luck. Any tips or suggestions will be appreciated.
if([answer containsObject:#"#"]){
/// do function.
}
You can check if an NSArray contains an object with containsObject. If it's an array of characters represented as one-character strings, then the code is simple:
NSArray *array = #[#"a", #"b", #"c", #"d"];
BOOL contains = [array containsObject:#"c"];
There's no such thing as an NSArray of scalar types like 'c' char, since the NS collections contain only objects. The nearest thing to an array of chars is an NSString, which has a variety of ways to tell you if a character is present. The simplest looks like this:
NSString *string = #"test#test.com";
NSRange range = [string rangeOfString:#"c"];
BOOL contains = range.location != NSNotFound;
You have to cycle through each NSString in the array and check if it contains the substring.
This custom method shows how:
//assumes all objects in the array are NSStrings
- (BOOL)array:(NSArray *)array containsSubstring:(NSString *)substring {
BOOL containsSubstring = NO;
for (NSString *string in array) {
if ([string rangeOfString:substring].location != NSNotFound) {
containsSubstring = YES;
break;
}
}
return containsSubstring;
}
Usage:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:3];
[array addObject:#"hi"];
[array addObject:#"yo"];
[array addObject:#"test#test.com"];
BOOL containsSubstring = [self array:array containsSubstring:#"#"];
You could create a custom Category class of NSArray and add the following method:
- (BOOL) containsCharacter:(NSString *) character
{
BOOL characterFound = NO;
for (id object in self)
{
if ([object isKindOfClass:[NSString class]])
{
NSRange range = [object rangeOfString:character];
if (range.location != NSNotFound)
{
characterFound = YES;
break;
}
}
}
return characterFound;
}
Thanks,
Michael
I think that is better way - to use predicates for filtering your array as Larme said.
Try something like this:
NSArray *answer = #[#"John Appleseed", #"john#apple.com", #"john#icloud.com", #"+14120123456", #"invalid#email", #"another###invalid.email"];
NSArray *filteredArray = [answer filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF CONTAINS \"#\""]];
if (filteredArray.count > 0) {
// Do something
}
The filteredArray will contains all objects, which contains at-symbol:
john#apple.com,
john#icloud.com,
invalid#email,
another###invalid.email
Another way - is to filter array by valid email strings and not only at-symbol:
NSArray *answer = #[#"John Appleseed", #"john#apple.com", #"john#icloud.com", #"+14120123456", #"invalid#email", #"another###invalid.email"];
NSString *emailRegex = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSArray *filteredArray = [answer filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex]];
if (filteredArray.count > 0) {
// Do something
}
The filteredArray will contains only objects, which matches to email-mask
john#apple.com,
john#icloud.com
I've populated a uitableview from an NSMutableDictionary using this code to populate my dictionary:
-(NSMutableDictionary *)createDictionaryForSectionIndex:(NSArray *)array withSubtitle:(NSArray *)subtitle
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (char firstChar = 'a'; firstChar <= 'z'; firstChar++)
{
NSString *firstCharacter = [NSString stringWithFormat:#"%c", firstChar];
NSArray *content = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF beginswith[cd] %#", firstCharacter]];
NSMutableArray *mutableContent = [NSMutableArray arrayWithArray:content];
if ([mutableContent count] > 0)
{
NSString *key = [firstCharacter uppercaseString];
[dict setObject:mutableContent forKey:key];
NSLog(#"%#: %lu", key, (unsigned long)[mutableContent count]);
}
NSLog(#"mutable content: %#", mutableContent);
}
return dict;
}
from this source: https://stackoverflow.com/a/13279408/1042801
I'm trying to add another key/value for each outputted key/value. It's somewhat difficult to articulate my thoughts, so here's an example of something that I think I need:
key => 'A'
object => {"Aardvark"
object => {"mammal"}
, "Ape"
object => {"mammal"}
, "Aquaman"
object => {"superhero"}
}
//etc...
My table view should then be able to access those keys for each object above it to use as a detail text label for the cells in my table view. My apologies if what I'm trying to accomplish makes no sense, dictionaries aren't quite engrained in my repertoire yet.
I have this for loop that gives me two strings when it is done. However I need to add these two strings into an array. The first string is one and second string is two. So at the end, my NSMutableArray needs to contain both one and two.
How can I perform this correctly?
for (NSDictionary* dict in dictArray) {
NSString *string = ([dict valueForKey:#"string"] == [NSNull null]) ? #"" : [NSString stringWithFormat:#"%#", [dict valueForKey:#"string"]];
}
I also suggest you to simplify your code
NSMutableArray *results = [NSMutableArray array];
for (NSDictionary* dict in dictArray) {
id value = dict[#"string"];
[results addObject: value == [NSNull null] ? #"" : value];
}
First, you need an array to add the string to:
NSMutableArray *resultsArray = [NSMutableArray array];
then you need to add the string to it, in the loop:
[resultArray addObject:string];
I have an array which contains multiple Dictionaries each one with 3 keys (#"date", #"username", #"text").
What I want to check for, is whether the same user (#"username") exists in more than one dictionary in that Array. And, if she does, combine the text for those "duplicates" into one dictionary.
I have considered this answer to check for duplicates and this one
but I cannot figure out how to combine these two.
Jumping in here because although I think you should work on the code yourself first, I think Miro's answer is more complicated than the issue requires and though I like the idea of using predicates in Greg's answer, here's a 3rd solution that (1) wouldn't require you to change your data structure and (2) references the necessary loops...
The way I'd do it: Create an NSMutableArray then start adding the usernames in order. If the NSMutableArray already contains the username though, don't add another instance of the username, but instead merge the dictionary info.
ex.
// Note: I'm calling your array of user dictionaries userArray.
// Create a username array to store the usernames and check for duplicates
NSMutableArray *usernames = [[NSMutableArray alloc] init];
// Create a new userArray to store the updated dictionary info, merged
// entries et. al.
NSMutableArray *newUserArray = [[NSMutableArray alloc] init];
// Go through the array of user dictionaries
for (NSDictionary *userDict in userArray) {
// If the usernames array doesn't already contain the username,
// add it to both the usernames array and the newUserArray as is
if (![usernames containsObject:[userDict objectForKey:#"username"]]) {
[usernames addObject:[userDict objectForKey:#"username"]];
[newUserArray addObject:userDict];
}
// Otherwise, merge the userArray entries
else {
// Get a mutable copy of the dictionary entry at the first instance
// with this username
int indexOfFirstInstance = [usernames indexOfObject:[userDict objectForKey:#"username"]];
NSMutableDictionary *entry = [[newUserArray objectAtIndex:indexOfFirstInstance] mutableCopy];
// Then combine the "text" or whatever other values you wanted to combine
// by replacing the "text" value with the combined text.
// (I've done so with a comma, but you could also store the value in an array)
[entry setValue:[[entry objectForKey:#"text"] stringByAppendingString:[NSString stringWithFormat:#", %#", [userDict objectForKey:#"text"]]] forKey:#"text"];
// Then replace this newly merged dictionary with the one at the
// first instance
[newUserArray replaceObjectAtIndex:indexOfFirstInstance withObject:entry];
}
}
Maybe something like this [untested] example? Loop through, maintain a hash of existing items, and if a duplicate is found then combine with existing and remove.
NSMutableArray main; // this should exist, with content
NSMutableDictionary *hash = [[NSMutableDictionary alloc] init];
// loop through, backwards, as we're attempting to modify array in place (risky)
for(int i = [main count] - 1; i >= 0; i--){
// check for existing
if(hash[main[i][#"username"]] != nil){
int existingIdx = [hash[main[i][#"username"]] integerValue]; // get existing location
main[existingIdx][#"text"] = [main[existingIdx][#"text"] stringByAppendingString:main[i][#"text"]]; // "combine text" .. or however you'd like to
[main removeObjectAtIndex:i]; // remove duplicate
} else {
[hash setValue:[[NSNumber alloc] initWithInt:i] forKey:main[i][#"username"]]; // mark existance, with location
}
}
If you use NSMutableDictionary, NSMutableArray and NSMutableString you can do it with predicate like that:
NSMutableDictionary *d1 = [#{#"username": #"Greg", #"text" : [#"text 1" mutableCopy]} mutableCopy];
NSMutableDictionary *d2 = [#{#"username": #"Greg", #"text" : [#"text 2" mutableCopy]} mutableCopy];
NSMutableDictionary *d3 = [#{#"username": #"John", #"text" : [#"text 3" mutableCopy]} mutableCopy];
NSMutableArray *array = [#[d1, d2, d3] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username = %#", #"Greg"];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary * firstDict = filterArray[0];
for (NSDictionary *d in filterArray)
{
if (firstDict != d)
{
[firstDict[#"text"] appendString:d[#"text"]];
[array removeObject:d];
}
}