I am creating an NSArray of NSStrings, however one of the arrays that is entered is a set of quotation marks:
""
I would like to know hot to exclude these from my array, I have tried using a predicate but it's not working.
This is what my code looks like:
NSString *tempSymbolsString = [tempAxesDictionary objectForKey:#"Symbols"];
NSArray *tempSymbolsArray = [tempSymbolsString componentsSeparatedByString:#";"];
tempSymbolsArray = [symbolsArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != """""]];
NSLog(#"%#", tempSymbolsArray);
Actually is even simpler than this. Since you only got strings in your array, create a mutable copy and remove all occurrences of "". Something like this perhaps:
NSMutableArray *temp = [tempSymbolsArray mutableCopy];
[temp removeObject:#"\"\""];
This works since removeObject: will compare objects via isEqual: and remove any matches.
Do it yourself:
NSString *tempSymbolsString = tempAxesDictionary[#"Symbols"];
NSMutableArray *symbolsArray = [[tempSymbolsString componentsSeparatedByString:#";"] mutableCopy];
for (NSUInteger i = symbolsArray.count; i > 0; i--) {
if ([symbolsArray[i - 1] isEqualToString:#"\"\""]) {
[symbolsArray removeObjectAtIndex:i - 1];
}
}
At the end the symbolsArray will have all values except those matching "".
BTW - your original predicate probably needs a bunch of escaping:
[NSPredicate predicateWithFormat:#"SELF != \"\\\"\\\"\""]
Related
In my app, I have a UITableView that is currently set to show everything inside the Downloads folder that is a PDF. What I'd like to do is have it show everything UNLESS it contains a specific word. I know I can use NSPredicate to make an NSArray that ONLY has that word, but how can I do the opposite? I've tried making it an NSMutableArray and then looping through it to remove all objects that contain that word, but it isn't working.
NSArray *theNewArray22 = [thefirstNewArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self ENDSWITH 'Kids.pdf'"]];
NSMutableArray *arr = [NSMutableArray arrayWithArray:theNewArray22];
for (int i = 0 ; i< arr.count; i++) {
[arr removeObjectAtIndex:i];
}
NSArray *resultArr = [arr mutableCopy];
I was thinking that since the predicate should return ONLY those that end with Kids.pdf, that looping through it to remove each object would work, but for some reason I can't get it.
I have a tableview that i want to search through with a searchable. It worked before but when i added sections i got into trouble because i had to change from arrays to dictionary.
So basically i have a NSDictionary that looks like this
{ #"districtA": array with point objects, #"districtB": array with point objects}
I need to filter them based on the point objects.name that is in the arrays. After that i want to create a new nsdictionary with the filtered objects in it.
I tried at least 10 different methods but i can't figure it out so i think this is the only way that i am most positive that should work.
This is the only way i can think of if there is an easier way or more logic way please tell me.
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//create new array to fill
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
//loop through the values and put into an rray based on the predicate
arrayWithFilteredPoints = [NSArray arrayWithObject:[[self.PointList allValues] filteredArrayUsingPredicate:predicate]];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:Point.district])
dict[Point.district] = [#[] mutableCopy];
[dict[Point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];}
This results in a crash, it happens of course where the predicate is used but i don't know how to debug what predicate i should use:
on 'NSInvalidArgumentException', reason: 'Can't do a substring operation with something that isn't a string (lhs = (
West ) rhs = w)'
I have read the comments and you are right. There was something wrong with my code.
I changed the logic, i added some more steps (like creating NSArray without needing it) to make the solution clear
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//1. create new array to fill only the Points from the dictionary
NSArray *allPoints = [self.PointList allValues];
NSMutableArray *allPointObjects = [[NSMutableArray alloc]init];
for (NSArray *array in allPoints) {
for (Point *point in array) {
[allPointObjects addObject:point];
}
}
//2. loop through allPointObjects and put into an mutablearray based on the predicate
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
arrayWithFilteredPoints = [allPointObjects filteredArrayUsingPredicate:predicate];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:point.district])
dict[point.district] = [#[] mutableCopy];
[dict[point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I wanted a filtered nsdictionary at the end that i can pass back to my tableview that reads the dictionary objects based on the keys (districts)
It seems clear from your description that [self.PointList allValues] is not an array of Point objects but an array of arrays of Point objects. That is the source of your difficulty, including your original crash.
You need to decide what to do about that; for example, if you want just one big array of Point objects, then flatten the array of arrays before you filter. I can't advise you further because it is not obvious to me what ultimate outcome you desire.
EDIT You've now modified your code and I can see more clearly what you're trying to do. You have a dictionary whose values are arrays of points, and you are trying to filter some of the points out of each array. What I would have done is to do that - i.e., run thru the keys, extract each array, filter it, and put it back (or delete the key if the array is now empty). But I can see that what you are doing should work, because you have cleverly put the keys into the points to start with, so you can reconstruct the dictionary structure from that.
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 have a app in which i want searching. I have a array resultArray which contain all the things which display like
Book*bookObj=[resultArray objectAtIndex.indexPath.row];
NSString*test=bookObj.title;
I want to perform search on title item in resultArray if search text enter in textfield matches with title with any of the arrays then copy those all array values in testArray.
Use this as :
NSMutableArray *searchDataMA = [NSMutableArray new];
for (int i = 0; i < resultArray.count; i++) {
Book *bookObj=[resultArray objectAtIndex:i];
NSString*test=bookObj.title;
NSRange rangeValue1 = [test rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (rangeValue1.length != 0) {
if (![resultArray containsObject:test]) {
[searchDataMA addObject:test];
}
}
}
You have to take another array for this. this will add your object
for (Book * bookObj in resultArray) {
NSString *strName=[[bookObj.title]lowercaseString];
if ([strName rangeOfString:searchText].location !=NSNotFound) {
[arrTemp addObject:bookObj];
}
}
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate is exactly the function that you want. It will return a new array with only the elements that pass the test in the NSPredicate object.
For example:
NSArray *newArray = [oldArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Book *evaluatedObject, NSDictionary *bindings) {
//Do whatever logic you want to in here
return [evaluatedObject.title isEqualToString:theTitle];
}];
It works for me try this:
NSArray *fruits = [NSArray arrayWithObjects:#"Apple", #"Crabapple", #"Watermelon", #"Lemon", #"Raspberry", #"Rockmelon", #"Orange", #"Lime", #"Grape", #"Kiwifruit", #"Bitter Orange", #"Manderin", nil];
NSPredicate *findMelons = [NSPredicate predicateWithFormat:#"SELF contains[cd] 'melon'"];
NSArray *melons = [fruits filteredArrayUsingPredicate:findMelons];
NSPredicate *findApple = [NSPredicate predicateWithFormat:#"SELF beginswith 'Apple'"];
NSArray *apples = [fruits filteredArrayUsingPredicate:findApple];
NSPredicate *findRNotMelons = [NSPredicate predicateWithFormat:#"SELF beginswith 'R' AND NOT SELF contains[cd] 'melon'"];
NSArray *rNotMelons = [fruits filteredArrayUsingPredicate:findRNotMelons];
NSLog(#"Fruits: %#", fruits);
NSLog(#"Melons: %#", melons);
NSLog(#"Apples: %#", apples);
NSLog(#"RNotMelons: %#", rNotMelons);
Predicates also have more condition functions, some of which I have only touched on here:
beginswith : matches anything that begins with the supplied condition
contains : matches anything that contains the supplied condition
endswith : the opposite of begins with
like : the wildcard condition, similar to its SQL counterpart. Matches anything that fits the wildcard condition
matches : a regular expression matching condition. Beware: quite intense to run
The syntax also contains the following other function, predicates and operations:
AND (&&), OR (||), NOT (!)
ANY, ALL, NONE, IN
FALSE, TRUE, NULL, SELF
Still if don't understand take a look at this link;
Useful link
I am trying to find the tags inside a NSDictionary inside myAr that matches the criteria of str and I want the result that has only those exact arrays no more nor less. In this example I want only the 2nd NSDictionary of myAr.
I though of trying to achieve this by using a predicate but that always returns empty when i use arrays.
I am trying to filter using an array but this is not working. I was wondering if anyone could tell me what i am doing wrong and how could i achieve my objective. thanks in advance
NSArray * myAr = #[ #{ #"tags": #[#"one",#"two",#"three"],
#"number": #"4"
},
#{ #"tags": #[#"one",#"two"],
#"number":#"4"
},
#{ #"tags": #[#"one",#"two",#"four"],
#"number":#"4"
},
#{ #"tags": #[#"chacho",#"chocho"],
#"number":#"4"
},
#{ #"tags": #[#"one"],
#"number":#"4"
} ];
NSArray* str = #[#"one",#"two"];
NSPredicate* pre = [NSPredicate predicateWithFormat:#"tags CONTAINS %# ",str];
myAr = [myAr filteredArrayUsingPredicate:pre];
NSLog(#"%#",myAr);
If I understand your question correctly, you just have to replace "CONTAINS" by "="
in the predicate:
[NSPredicate predicateWithFormat:#"tags = %# ",str]
This gives an array with all dictionaries where the "tags" value is equal to the
given array str. In your example, it returns an array with the second dictionary
only.
UPDATE: To find all dictionaries where the "tags" value is an array with the
given elements, but independent of the order, the following slightly more
complicated predicate should work:
[NSPredicate predicateWithFormat:#"tags.#count = %d AND SUBQUERY(tags, $t, $t in %#).#count = %d",
[str count], str, [str count]];
UPDATE 2: That was too complicated, the following predicate seems to work as well:
[NSPredicate predicateWithFormat:#"tags.#count = %d AND ALL tags in %#",
[str count], str]
(I have assumed that str contains only different elements.)
For an answer that uses neither a for loop nor predicate format strings, try using a block and make use of NSSet to determine if the set of tags you want to match is equal to a set of the array element's tags. For example:
NSSet* desiredTags = [NSSet setWithObjects:#"one", #"two", nil];
NSPredicate *tagFilterPredicate = [NSPredicate
predicateWithBlock:^BOOL (id data, NSDictionary *bindings) {
NSSet *tags = [NSSet setWithArray:[data objectForKey:#"tags"]];
return [desiredTags isEqual:tags];
}];
NSArray *resultArray = [myArr filteredArrayUsingPredicate:tagFilterPredicate];
Bear in mind that this does allocate a set per iteration. So, if you're looking to avoid allocations, this is not adequate. Otherwise, it at least avoids a format string.
A brute-force way to do this would be to remove your predicate and just enumerate:
NSArray *required = #[#"one", #"two"];
NSMutableArray *matches = [#[] mutableCopy];
[myAr enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
BOOL match = YES;
for (NSString *item in required) {
if (![obj containsObject:item]) {
match = NO;
break;
}
}
if (match && [(NSArray *)obj count] == required.count) {
[matches addObject:obj];
}
}
}];
}];