Extract row number of array when string searched using Predicate - ios

I am not sure how to ask the question, so perhaps if I say what the issue is someone out there can help.
I have a SQL lite table which I use to populate two arrays: one of names and surnames, and the other array, which gets populated at the same time, of the ID of the person from the database.
I then display these names in a TableView. I use the Predicate method to search for someones name in the original array (called storiesArray).
However, when I select this person to display, it displays the First person in the stories Array.
I have tried getting the row where the search string is found, but this returns a very large number, even though it does find the string (using Predicates as explained).
How can I get the row number of the found string?
So, I have tried the following code:
NSUInteger indexOfTheObject = [storiesArray indexOfObject: searchText];
But this returns the number 2147483647, which in effect is a -1.
I know that the searchText is in stories array because the following code does return a valid search result:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#", searchText];
self.searchResult = [NSMutableArray arrayWithArray:[storiesArray filteredArrayUsingPredicate:resultPredicate]];
}
self.searchResult gets populated with the correct string.
Thanks in advance.

Similar Threads here:
How retrieve an index of an NSArray using a NSPredicate?
how to find the index position of the ARRAY Where NSPredicate pick the value. I use filteredArrayUsingPredicate as filter
http://useyourloaf.com/blog/2010/10/19/searching-arrays-with-nspredicate-and-blocks.html
NSUInteger index = [self.myarray indexOfObjectPassingTest:
^(id obj, NSUInteger idx, BOOL *stop) {
return [predExists evaluateWithObject:obj];
}];

Related

NSPredicate String to get n'th item in array

I cannot find a way to get the nth item in an array using an NSPredicate string. For example:
//You cannot touch or modify the code inside this method. You can only use the predicate string param to filter the array.
- (NSArray *)filterUsingNSPredicate:(NSString *)PredicateString
{
NSArray *array = #[
#"firstItem",
#"secondItem",
#"thirdItem",
#"fourthItem",
];
NSPredicate *pred = [NSPredicate predicateWithFormat:PredicateString];
NSArray *filtered = [array filteredArrayUsingPredicate:pred];
NSLog(#"This is the second item in the array! %#",filtered); //Thats the only thing in the array.
return filtered;
}
If you want to get an item, you don't receive an NSArray, because you will only receive an object.
You can use NSPredicate to search by name of an object in your array. But what you say, that is not what you want.
You can use [array objectAtIndex:index];, that returns the object in the position you indicate in index.
If you want the index of an object, you can use [array indexOfObject:object];, that returns the index of the object.
Predicates don't know about array indexes. They only know about the single object that they're presented with at any one time, and whether that object makes the predicate true or false.
The way you're presenting this problem in the comments makes absolutely no sense. If you can get a filtered version of the array, then you can get the array. Here's how you use the method you show to do so:
NSArray * fullList = [theAPI filterUsingNSPredicate:#"TRUEPREDICATE"];
TRUEPREDICATE is a special value for predicate strings that always evaluates to true. When you filter an array with that predicate, the result will be identical to the original.
You now have a reference to the array, and can index into it as you would normally.
You can create predicate with block https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/#//apple_ref/occ/clm/NSPredicate/predicateWithBlock%3A like this to get 6-th element (at index 5 counting from 0):
__block NSInteger index = 0;
NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *, id> *bindings)(
return index++ == 5;
)];

How to filter dictionary values based on another dictionary key from a JSON?

I have a table with a UISearchController that searches it. The first table is populated from a JSON with a format of:
{
"items":
[
{
"title":"title1",
"url":"url1",
},
{
"title":"title2",
"url":"url2",
}
]
}
The "title" is shown as the cell's textLabel and the url is the link that opens when the cell is clicked.
When I search in the search bar a results table shows populated by the the titles that match the search criteria. My problem is these don't include the urls so nothing happens when these cells are clicked. My search criteria is as follows:
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
// filter the search results
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains [cd] %#", self.searchController.searchBar.text];
self.results = [[self.JSONarray valueForKey:#"title"] filteredArrayUsingPredicate:predicate];
[self.tableView reloadData];
}
I can see what I think is the problem but cannot figure out how to fix it. When searching it is only searching through the titles and populating the results array with these but I need the urls to filter based on the corresponding titles.
Any help would be really appreciated.
I'm typing this off the top of my head since I don't have all your code, but try the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title contains [cd] %#", self.searchController.searchBar.text];
self.results = [self.JSONarray filteredArrayUsingPredicate:predicate];
The difference is that we're not filtering on the title elements of the JSON array first; instead, we're searching the whole array, and using title in the predicate.
After doing this, self.results should be an NSArray of NSDictionarys, each of which contains a title and url element. So you can access the URLs as appropriate to retrieve your results.

use NSPredicate to filter object

Here say I have a array of objects with two attributes:
// array of object
NSArray *objects
// object
NSString *primaryTag;
NSArray *secondaryTag;
Since what I want is when the this object contains the givenTag, it could be passed to a new array called results;
Here is my codes:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
results = [objects filteredArrayUsingPredicate:resultPredicate];
It seems that the primaryTag works well, but the secondaryTag doesn't work, can someone help me out. I am not that familiar with NSPredicate filtering. Thanks in advance.
The most efficient way to do that is with a NSCompoundPredicate like so:
NSArray *subPredicates = #[tag1, tag2, tag3];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Your question is a little unclear so you might also want:
andPredicateWithSubpredicates
Depending on exactly what the nature of the result set you are looking for.
See Apple Docs here: NSCompoundPredicate Docs
i implemented the following custom class:
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString *primaryTag;
#property (strong, nonatomic) NSArray *secondaryTag;
#end
and overrode it's description method for the NSLog statement to print something we understand:
- (NSString *)description {
return [NSString stringWithFormat:#"primaryTag: %#, secondaryTag: %#", _primaryTag, [_secondaryTag componentsJoinedByString:#", "]];
}
then i created some objects from the custom class and added them to an array:
NSMutableArray *objects = [NSMutableArray array];
CustomObject *obj1 = [CustomObject new];
obj1.primaryTag = #"stringToSearchFor";
obj1.secondaryTag = #[#"notTheStringToSearchFor", #"somethingElse"];
[objects addObject:obj1];
CustomObject *obj2 = [CustomObject new];
obj2.primaryTag = #"differentString";
obj2.secondaryTag = #[#"nothingWeAreLookingFor"];
[objects addObject:obj2];
CustomObject *obj3 = [CustomObject new];
obj3.primaryTag = #"anotherOne";
obj3.secondaryTag = #[#"whoCaresForThisString", #"stringToSearchFor"];
[objects addObject:obj3];
finally i created a string to search for and the predicate:
NSString *givenTag = #"stringToSearchFor";
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
when i log out the result i get the correct results:
NSLog(#"%#", [objects filteredArrayUsingPredicate:resultPredicate]);
logs:
(
"primaryTag: stringToSearchFor, secondaryTag: notTheStringToSearchFor, somethingElse",
"primaryTag: anotherOne, secondaryTag: whoCaresForThisString, stringToSearchFor"
)
which is obj1 and obj3. correct! if it does not work for you there's gotta be something else wrong with your code...
If my understanding of the original question is incorrect, please let me know, and I will adjust my answer.
Problem: You have an array of objects with 2 properties. One is primaryTag, which is a string. The second is an array of secondaryTags, which is a collection of strings. You want to filter all objects where either the primaryTag matches, or where the search string matches one of the secondaryTags.
Answer The proper way to match strings is via MATCHES or CONTAINS.
NSPredicate *pPredicate =
[NSPredicate predicateWithFormat:#"%K CONTAINS[cd] %#",
#"primaryTag", searchString];
NSPredicate *sPredicate =
[NSPredicate
predicateWithFormat:#"SUBQUERY(%K, $st, $st CONTAINS[cd] %#).#count > 0",
#"secondaryTags", searchString];
NSCompoundPredicate *searchPredicate =
[NSCompoundPredicate orPredicateWithSubPredicates:#[ pPredicate, sPredicate ]];
How it works: The first predicate is a straightforward match. You can replace CONTAINS with MATCHES, if that better fits the kind of comparison you wish to make. The [cd] suffix means case-insensitive and diacritic-insensitive. It's normal to include those when searching/filtering, but again, it's up to you. Instead of embedding the property name in the predicate format string, I use %K and a replacement parameter. In production code, that replacement parameter would be a constant.
The second predicate is a little trickier. It uses a SUBQUERY() to filter the secondaryTags array, and returns the object as matching if at least one secondary tag matches the search string. SUBQUERY() is a function with 3 parameters. The first is the collection being searched. The second is a temporary variable that represents each item in the collection, in turn; it is used in the 3rd parameter. The 3rd parameter is a regular predicate. Each item in the collection that matches the filter is included in the output of SUBQUERY(). At the end, the matching secondary tags are counted (via #count), and if the count is greater than zero, the original object is considered to have matched, so will be included in the filtered output.
Finally, we combine these two predicates into one searchPredicate, which can now be used to filter your array of objects.
I seen this issue,
My normal approch is to use the NSPredicate twice,
So that I can track the result at every steps:
Option 1:
NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"primaryTag == %#", givenTag];
results1 = [objects filteredArrayUsingPredicate:resultPredicate1];
NSPredicate *resultPredicate2 = [NSPredicate predicateWithFormat:#"secondaryTag CONTAINS[c] %#", givenTag];
finalResults = [results1 filteredArrayUsingPredicate:resultPredicate2];
Option 2:
Use NSCompoundPredicate to compound multiple filtering. You can easily find many examples on google and stackOverFlow.
Hope this will help,
Thanks

KVC to add values from multiple Keys to NSMutableArray

I want to display name and lastName in my UITableViewCell. I have an array with a lot of data that I retrieve from a database, and pretty much everything I need is in the array, the trick is to filter and show the results as I want.
I have the following code to filter what is being typed on searchBar and add to an NSMutableArray:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"usuario.username contains [cd] %#", searchText];
NSArray *filtroUsuario = [name filteredArrayUsingPredicate:predicate];
searchResults = [filtroUsuario valueForKeyPath:#"#distinctUnionOfObjects.usuario.nome"];
I use #distinctUnionOfObjects because the objects that I'm filtering are not user objects, therefore I want to retrieve user values, so as some objects point to the same user, I get duplicate names.
The code to put information on the UITableView is like this:
cell.textLabel.text = searchResults[indexPath.row];
It all works fine. My trouble is that now I want to show one more key on the cell, so the keypath would be usuario.sobrenome. How would I put both values together?
I've tried playing with the line:
searchResults = [filtroUsuario valueForKeyPath:#"#distinctUnionOfObjects.usuario.nome"];
and got some interesting results, but not the one I'm expecting.
It looks like you should be able to use #distinctUnionOfObjects.usuario, so your results is an array of user objects (or some other objects with the keys that you require). Then in the table view cell setup you do:
id user = searchResults[indexPath.row];
cell.textLabel.text = [user valueForKey:#"nome"];
cell.otherTextLabel.text = [user valueForKey:#"sobrenome"];

NSPredicate array contains question [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Searching/Filtering a custom class array with a NSPredicate
I have a array that contains objects of a custom class, and I would like to filter the array based on if one of the classes attributes contains a custom string. I have a method that is passed the attribute that I want to be searched (column) and the string that it will search for (searchString). Here is the code I have:
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# contains %#", column, searchString];
NSMutableArray *temp = [displayProviders mutableCopy];
[displayProviders release];
displayProviders = [[temp filteredArrayUsingPredicate:query] mutableCopy];
[temp release];
For some reason, this always returns an empty array. Three of the four columns are ints or doubles, so I could add an if statment to convert searchString to a number, if needed. However, even when searching the attribute that is a string, and should have some rows that contain the string searchString, an empty array is returned.
Am I using "contains" incorrectly?
For searching strings in a predicate, i've found that wrapping single quotation marks around what I am searching for works best and using contains[cd] rather than contains like below (the [cd] bit specifies a case & diacritic insensitive search):
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# contains[cd] '%#'", column, searchString];
For searching a column with Integers, using double equals should work fine too:
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# == %#", column, searchString];
I think your predicate should be [NSPredicate predicateWithFormat: #"column contains %#", searchString]
The way it is right now, your predicate becomes ""column" contains "string"", which always evaluates to false.
Using contains[cd] may also be a good idea depending on your situation.

Resources