NSPredicate limit results - iphone

I am trying to limit array of objects getting with [NSArray filteredArrayUsingPredicate] for better performance.
My Code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.Name contains[c] %#", searchText];
NSArray *filteredArray = [self.dataArray filteredArrayUsingPredicate:predicate];
My dataArray contains about 30.000 objects and I want to limit result array 50. (I need some break statement after 50 match found.) How can I achieve this?

Use simple for-in loop, NSMutableArray builder and -[NSPredicate evaluateWithObject:] method.
NSMutableArray *builder = [NSMutableArray arrayWithCapacity:50];
for (id object in array) {
if ([predicate evaluateWithObject:object]) {
[builder addObject:object];
if (builder.count >= 50) break;
}
}
I noticed you tagged the question by Core Data. If you can use NSFetchRequest, then just set its fetchLimit to 50.

How about this?
myArray = [originalArray filteredArrayUsingPredicate:myPredicate];
if ([myArray count] > resultLimit) {
myArray = [myArray subarrayWithRange:NSMakeRange(0, resultLimit)];
}

Related

Searching through an array and putting it into UITableViewCell in swift

Right now I have an array that has around 100000 elements. I've created a search bar for it but I think due to the number of elements that it is parsing through my phone lags when using the search bar. Is there a way to fix this?
Have you tried using NSPredicate?
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"Ben", #"Adam", #"Melissa", nil];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] 'a'"];
NSArray *beginWithA = [array filteredArrayUsingPredicate:bPredicate];
// beginWithA contains { #"Adam" }.
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'e'"];
[array filterUsingPredicate:sPredicate];
// array now contains { #"Nick", #"Ben", #"Melissa" }
That being said, with 100000 elements, wouldn't a persistent store make more sense?

NSPredicate of NSArray in NSDictionary

My Array
(
{id:1,data:(#"macbook",#"mac mini")},
{id:2,data:(#"ipad",#"ipod")},
{id:3,data:(#"macbook",#"ipod")}
)
I have a predicate
NSString *text = #"mac";
[NSPredicate predicateWithFormat:#"(data contains[cd] %#)",text];
[array filteredArrayUsingPredicate:predicate];
but it doesn't loop over my array inside my dictionary
(my result should be an array containing 2 objects with id 1 and 3)
NSString* text = #"mac" ;
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"any data contains[cd] %#",text] ;
NSArray* filteredArray = [theArray filteredArrayUsingPredicate:predicate] ;
I personally find NSPredicate formats very error prone.
You may consider using a block in order to filter your NSArray
NSString * filterString = #"mac";
NSIndexSet * indexes = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSArray * entry = obj[#"data"];
for (NSString * value in entry)
if ([value rangeOfString:filterString].location != NSNotFound)
return YES;
return NO;
}];
NSArray * filteredArray = [array objectsAtIndexes:indexes];
It's definitely longer and more verbose, but I find it definitely easier to read and debug.

What's the easiest way to remove empty NSStrings from an NSArray?

In PHP it's one line of code:
$array_without_empty_strs = array_filter($array_with_empty_strs);
What's the objective C equivalent?
UPDATE - Added the following test code to illustrate the use of Nikolai Ruhe's solution:
// SOLUTION Test Code
NSMutableArray *myArray = [[NSMutableArray alloc] init ];
[myArray addObject:[NSNumber numberWithInt:5]];
[myArray addObject:#""];
[myArray addObject:#"test"];
NSLog(#"%#", myArray);
[myArray removeObject:#""];
NSLog(#"%#", myArray);
// SOLUTION Test Code Output
2012-07-12 08:18:16.271 Calculator[1527:f803] (
5,
"",
test
)
2012-07-12 08:18:16.273 Calculator[1527:f803] (
5,
test
)
It's even more simple:
[mutableArrayOfStrings removeObject:#""];
If your array is not mutable you have to create a mutableCopy before.
removeObject: removes all objects from an array that return YES from isEqual:.
NSArray *noEmptyStrings = [maybeEmptyStrings filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"length > 0"]];
we can use NSPredicate
here my code
NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(id str, NSDictionary *unused) {
return ![str isEqualToString:#""];
}];
NSArray *filtered = [yourArray filteredArrayUsingPredicate:pred];
Look at this question, which is almost exactly the same as yours.
I use this method for non-string (object) cases that don't work with NSPredicate
Where array is NSMutableArray
- (void) clearArrayOut
{
for (int j=[array count]-1; j>=0; j--)
if ([[array objectAtIndex:j] length] == 0)
[array removeObjectAtIndex:j];
}
And I totally just stole this from rob mayoff's beautiful answer on my question NSPredicate instead of loop to filter an array of objects
Here's an alternate method that is more flexible even than using a predicate.
NSArray* filteredArray = [arrayToFilter objectsWithIndexes:
[arrayToFilter indexesOfObjectsPassingTest:
^((id obj, NSUInteger idx, BOOL *stop)
{
return [obj length] > 0;
}]];

Search NSArray for value matching value

I have an NSArray of objects, which has a particular property called name (type NSString).
I have a second NSArray of NSStrings which are names.
I'd like to get an NSArray of all the objects whose .name property matches one of the names in the second NSArray.
How do I go about this, fast and efficiently as this will be required quite often.
Why not just to use predicates to do that for you?:
// For number kind of values:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF = %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
// For string kind of values:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
// For any object kind of value (yes, you can search objects also):
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
Here's a simple way:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", nameToFind];
[listOfItems filteredArrayUsingPredicate:predicate];
With your current data structures, you can only do it in O(n^2) time by looping over the first array once for each member of the second array:
NSMutableArray * array = [NSMutableArray array];
for (NSString * name in names) {
for (MyObject * object in objects) {
if ([[myObject name] isEqualToString:name]) {
[array addObject:object];
}
}
}
(Alternate as suggested by Stefan: loop over the objects array and ask the names array if it containsObject: for the name of each object.)
But if this really needs to be faster (really depends on the size of the arrays as much as how often you do it), you can improve this by introducing an NSDictionary that maps the names in the first array to their objects. Then each of those lookups is O(1) and the overall time is O(n). (You'd have to keep this dictionary always in sync with the array of objects, which isn't hard with reasonable accessors. This technique also has the constraint that the same name can't appear on more than one object.)
An alternate way of getting this result (and which doesn't have that last constraint) is to use an NSSet for your second collection, then walk through the objects array calling containsObject: with each one on the set of names. Whether this technique is better depends on whether your two collections are roughly the same size, or if one is much larger than the other.
I like to use this method:
NSIndexSet *indexes = [_items indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return ((MyObject *)obj).name isEqualToString:name];
}];
if (indexes.count != 0) {
//extract your objects from the indexSet, and do what you like...
}
NSMutableArray * foundNames = [NSMutableArray array];
for (MyObject * objectWithName in objectCollection) {
if ([names containsObject:objectWithName.name]) {
[foundNames objectWithName];
}
}
The methods most helpful will be:
filteredArrayUsingPredicate:
and
indexesOfObjectsPassingTest:
The second one uses a code block, not available on iOS before 4.0
Both of these will be more efficient than iterating directly.
There's a good example here:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxUsing.html
NSMutableArray* solutions = [NSMutableArray array];
for (Object* object in objects){
for (NSString* name in names){
if ([object.name isEqualToString:name]){
[solutions addObject:object];
break; // If this doesnt work remove this
}
}
}
int count=0;
if (range.location!=NSNotFound)
{
[searchindex addObject:[NSString stringWithFormat:#"%d",count]];
}

NSCompoundPredicate fails to match

I'm building a NSPredicate using the code below for an iPhone app. The logging shows the prediate to be: location CONTAINS "head" AND shape CONTAINS "oval" AND texture CONTAINS "bumpy" AND colour CONTAINS "red"
I get no results. If I limit the predicate to a single item it will work, more than 1 fails.
Can anyone tell me why?
Many thanks
NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
for (Ditem in self.tableDataSource) {
NSString *Title = [Ditem valueForKey:#"Title"];
NSString *Value = [Ditem valueForKey:#"Value"];
if([[Value lowercaseString] isEqualToString: #"all"]){
Value = #"";
}
else{
NSPredicate *p = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:[Title lowercaseString]] rightExpression:[NSExpression expressionForConstantValue:[Value lowercaseString]] modifier:NSDirectPredicateModifier type:NSContainsPredicateOperatorType options:0];
[subPredicates addObject:p];
}
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
NSLog(#"predicate: %#", predicate);[self.fetchedResultsController.fetchRequest setPredicate:predicate];
Your predicate is requiring that all of the values in your filterable objects be strings. Is that correct?
Also, I would simplify your subpredicate creation to:
NSPredicate * p = [NSPredicate predicateWithFormat:#"%K CONTAINS %#", [Title lowercaseString], [Value lowercaseString]];