I'm trying to implement lazy instantiation within my code to speed up my app. My question is, when dealing with a for loop, should I create an object right before the loop begins, or within the loop, basically should I do this:
NSPredicate *pred= [NSPredicate predicateWithFormat:#"self LIKE %#", filter];
for(NSString* str in myArray){
//do stuff
}
or this:
for(NSString* str in myArray){
//do stuff
NSPredicate *pred= [NSPredicate predicateWithFormat:#"self LIKE %#", filter];
//do stuff that needs this variable
}
My thoughts were to do the first one, but a friend says the 2nd one, although isn't the 2nd one creating the object everytime you go through the loop so it's more costly?
Neither one of these is truly lazy:
The first one creates the predicate even when the loop does not need to execute at all
The second one is optimal only when the loop executes once; otherwise, throw-away instances of NSPredicate get created.
If you wish to stay truly lazy, you could add a condition around the first code example to check that myArray is not empty. You could also use a conditional expression, like this:
NSPredicate *pred= myArray.count ? [NSPredicate predicateWithFormat:#"self LIKE %#", filter] : nil;
This is not particularly readable, though, so I would recommend staying with your first approach, which is optimal when the loop is entered at least once.
Well, lazy instantiation would look something like this I think...
NSPredicate *pred = nil;
for (NSString *string in myArray) {
// do stuff...
// if predicate is required
if (!pred) {
pred = [NSPredicate predicateWithFormat:#"self LIKE %#", filter];
}
// use pred
}
But out of the three options. I'd use your first method.
I don't see much of a choice, with the first way being both clean and simple. The equivalent alternative is actually:
NSPredicate *pred = nil;
for (NSString* str in myArray){
if (!pred)
pred = [NSPredicate predicateWithFormat:#"self LIKE %#", filter];
//do stuff
}
Which is clearly more complicated than it needs to be.
I would also argue that this isn't really lazy initialization, which really relates to initialization of instance variables at some point after object creation.
Related
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
I wanna extract from an array of SKSpriteNode only the elements that intersect with a predeterminated frame. I can do it by a for iteration:
for (SKSpriteNode* Object in Array) {
if (CGRectIntersectsRect(Frame,Object.frame)) {
//extraction code
}
}
However performace of this method seems to be poor,there's a way to do this operation in a faster way? I have tried something like this:
NSPredicate *Predicate = [NSPredicate predicateWithFormat:#"CGRectIntersectsRect(Frame,SELF.frame)"];
NSArray *Results = [Array filteredArrayUsingPredicate:Predicate];
But this create the error "Unable to parse function name 'CGRectIntersectsRect' into supported selector (CGRectIntersectsRect)". What's wrong? Using a predicate instead a for will give me some gain in performance?
Since predicate parser cannot recognize a free-standing C function, you can make a predicate from a block:
NSPredicate *intersects = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
return CGRectIntersectsRect(Frame, obj.frame);
}];
NSArray *results = [Array filteredArrayUsingPredicate:intersects];
I am not sure about the performance gain in comparison to the loop, though, because the number of comparisons is going to remain unchanged.
I'm building a customised UITableViewController that shows all the contacts in the iPhone and behaves like the ABPeoplePickerNavigationController. Meaning it also supports searching the contacts. I'm doing this with the code here.
I've implemented the search ability using Search Bar and Search Display Controller, and I followed this tutorial by appcoda.
Since my NSArray is an array of ABRecordRef my method of filterContentForSearchText: scope: is this:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
ABRecordRef person = (__bridge ABRecordRef)evaluatedObject;
NSString * fullname = [self getFullnameOfRecord:person];
NSPredicate *tmpPredicate = [NSPredicate predicateWithFormat:#"self contains[c] %#", searchText];
if ([tmpPredicate evaluateWithObject:fullname]) {
return YES;
} else {
NSLog(#"tmpPredicate didn't match");
return NO;
}
}];
searchResults = [self.allContacts filteredArrayUsingPredicate:resultPredicate];
}
The search results are fine, but since this is a very large array, it works very slowly. Is there a way I could improve the performance of this search mechanism?
Update: As #Eiko suggested, I tried replacing the inner NSPredicate with this code:
NSRange range = [fullname rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.length > 0) {
return YES;
} else {
return NO;
}
But it didn't improve the performance.
You should try to use the profiler to find the weakest line,
but I assume that the problem is that predicate block is evaluated for every entry every time.
I would suggest you to create your own wrapper class for ABRecordRef (let's say RecordWrapper), which will contain link to ABRecordRef with whole data and cache some frequent used and important values (e.g. fullName), you can obtain it once while loading list of contacts.
Then if you have an array of RecordWrapper* objects you can filter simply by calling
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"fullName contains[c] %#", searchText];
searchResults = [self.allContactsWrappers filteredArrayUsingPredicate:resultPredicate];
This should significantly increase filtering speed.
"very large array"? I'd guess that a contact list is rather small, typically < 1k elements.
That being said, the predicate stuff probably come at a premium, and with respect to this answer a simple enumuration might be the fastest. I suggest to test (and profile) on a real device, though.
I guess that creating predicates can be a costly operation (do they need to be compiled?), so you could reuse it, or even better, just do the little "contains check" on fullname yourself (just do a search with rangeOfString:options:), omitting the predicate alltogether.
I have an array(NSArray) let's say
NSArray *cityArry = #[#"Chennai", #"Mumbai", #"Kolkata"];
For example I have mentioned the NSArray with 3 objects, but actually in runtime I have an array which contain more than 1000 objects.
Now I have the string(NSString) "chennai Tnagar"
NSString *scanedStr = #"Chennai Tnagar";
So, now I want to find out that the whether range of scanedStr contain in cityArry. For example I should get output as #"Chennai" from the cityArry.
I know we can do this by looping the NSArray and by using rangeofstring we can get the output. Since I have more than 1000 objects I don't want to do the looping. Please guide me and help me out how to solve this.
Thanks in advance
You could basically split the string and search for the array on the cityArry.
NSString *scannedStr = #"Chennai Tnager";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF in[cd] %#", [scannedStr componentsSeparatedByString:#" "]];
[cityArray filteredArrayUsingPredicate: predicate];
I am certain that this is a duplicate of another answer, but I can't find it. If anybody can, please close this as a dupe.
Anyway, the IN operator works nicely on strings. Apparently it is undocumented, so you will have to decide if you want to rely on it:
NSArray *cityArray = #[#"Chennai",#"Mumbai",#"Kolkata"];
NSString *scannedStr = #"Chennai Tnagar";
NSPredicate *pred = [NSPredicate predicateWithFormat: #"SELF IN[cd] %#", scannedStr];
NSArray *result = [cityArray filteredArrayUsingPredicate: pred];
This solution has the advantage of keeping the object of the predicate on the left, which I happen to find easier to read - but as mentioned, it is not documented.
In case you wish to use a documented construct, you can swap the order of the predicate and use a normal CONTAINS:
pred = [NSPredicate predicateWithFormat: #"%# CONTAINS[cd] SELF", scannedStr];
I propose that you make use of SQL query like by using sqlite3 as a database of the list of all cities you have. Its a bit painful implementing a database but it solves the problem I think.
I have a model object Place that have a couple of standards properties. It also have some custom instance method defined in a Mogenerated subclass. That looks something like this:
- (NSString *) currentName {
return [self valueFromArrayData:self.nameLocaleMap atIndex:...];
}
I wonder if it's possible to make a NSpredicate would function something like this somehow:
// I have tried this predicate, and it doesn't work... IS it possible to do something similar to get the wanted effect?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"currentName CONTAINS[cd] %#", searchText];
Or alternatively, if it's possible to evaluate/compare the contents of a NSData property with a NSString?
Your predicate should work, but only when run locally against, for example, an array (filteredArrayUsingPredicate:).
Your predicate can not be used with Core Data because currentName doesn't exist at the data store level.