I have tried some combinations but can't seem to get it right. I am looking to combine line 2 and 3 into one line of code.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == 'SSM M51 Copperhead'"];
NSArray *searchResults1 = [self.weaponsArray filteredArrayUsingPredicate:predicate];
weapons = [searchResults1 objectAtIndex:0];
if(weapons.range > SSMrange)
SSMrange = weapons.range;
('weapons' is a class).
weapons = [self.weaponsArray filteredArrayUsingPredicate:predicate][0];
This isn't really going to make your code any faster though.
How about defining a method like:
- (Weapon*)firstWeaponMatchingPredicateWithFormat:(NSString*)format
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:format];
NSArray *searchResults1 = [self.weaponsArray filteredArrayUsingPredicate:predicate];
return (searchResults1.count > 0 ? [searchResults1 objectAtIndex:0] : nil);
}
Call with:
Weapon *weapon = [self firstWeaponMatchingPredicateWithFormat:#"name == 'SSM M51 Copperhead'"];
weapon = [[self.weaponsArray filteredArrayUsingPredicate:predicate] objectAtIndex:0];
There is no need to create an array object with the filtered results only to get the first object and throw away the collection.
If you're targeting iOS 4.0 or later, NSArray provides functionality to get the index of the first object passing a test through a block.
NSUInteger indexOfObject = [self.weaponsArray indexOfObjectPassingTest:^(id obj, NSUInteger index, BOOL *stop) {
return [[obj valueForKey:#"name"] isEqualToString:#"SSM M51 Copperhead"];
}];
The block will be executed once for each object and the array will stop processing when the block returns YES. Once you know the index of the object, you can get the value directly from you self.weaponsArray array. Just make sure you check the return value for NSNotFound.
I tried filtering an array of 1,000,000 objects and searching for the both the first and last object using the two approaches. Even when looking for the last object, the block approach was still quicker than using a predicate. I'm guessing due to the saving of not creating a filtered array.
Related
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;
)];
I have a class. Lets call that class MyObject. MyObject has a property called item that is a NSMetadataItem.
NSMetadataItems have an attribute called NSMetadataItemFSNameKey that can be fetched by using this:
NSString *fileName = [myMetadataItem valueForAttribute: NSMetadataItemFSNameKey];
Now I have an array or MyObjects and I want to find what object has an item which NSMetadataItemFSNameKey is the one I am looking for.
OK, I can iterate thru the array using this code:
for (MyObject *oneObj in array) {
NSString *oneFileName = [oneObj.item valueForAttribute:NSMetadataItemFSNameKey];
if ([oneFileName isEqualToString:fileNameItem]) {
// found, do something
}
}
but I am trying to find if it is possible to do that using NSPredicates and filtering?
Is that possible?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"item.%K = %#", NSMetadataItemFSNameKey, fileNameItem];
NSArray *filteresArray = [array filteredArrayUsingPredicate:predicate];
Typed in Safari.
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 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.
This question already has answers here:
Removing duplicates from NSMutableArray
(9 answers)
Closed 10 years ago.
I have an NSMutableArray which has entity class object as its objects.
Now I want to remove the distinct objects from it. Consider following example
Entity *entity = [[Entity alloc] init];
entity.iUserId = 1;
entity.iUserName = #"Giri"
[arr addObject:entity];
Entity *entity = [[Entity alloc] init];
entity.iUserId = 2;
entity.iUserName = #"Sunil"
[arr addObject:entity];
Entity *entity = [[Entity alloc] init];
entity.iUserId = 3;
entity.iUserName = #"Giri"
[arr addObject:entity];
Now I want only two objects in the Array by removing the duplicate iUserName. I know the way by iteration but I want it without iterating it like predicate or some other way.
If anyone knows then please help me.
I had tried using [arr valueForKeyPath:#"distinctUnionOfObjects.iUsername"]; but it does not return me the entired object.
This question is totally different than the questions which are asked previously. Previously asked question is for getting the distinct objects is correct but they uses looping & I don't want this. I want it from NSPredicate or any other simple option which avoids looping.
EDIT: You can't do what you want to without looping over the array manually and building up a new array. The answer below won't work because it assumes that there are only at most two duplicates.
NSMutableArray *filteredArray = [NSMutableArray array];
for (Entity *entity in arr)
{
BOOL hasDuplicate = [[filteredArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"iUserName == %#", entity.iUserName]] count] > 0;
if (!hasDuplicate)
{
[filteredArray addObject:entity];
}
}
This will look for duplicates in the filtered array as it builds it.
Begin Original Answer
You can't use an NSSet because the Entity instances would have to return NSOrderedSame in compareTo:, which isn't a good idea since you shouldn't use names as unique identifiers.
You can use predicates, but they'll still loop over the array in an O(n^2) time without some optimization.
NSArray *filteredArray = [arr filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Entity *evaluatedObject, NSDictionary *bindings) {
return [[arr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"iUserName == %#", evaluatedObject.iUserName]] count] > 1;
}]];
That'll work fine. You could make it even faster by sorting the array by the iUserName property first and doing a linear scan over the sorted array (stopping when you see the first duplicate). That's a lot of work if you're dealing with small sample sizes (say, under ten thousand or so). It's probably not worth your time, so just use the code above.
Well you have a few options (that I can think of).
Use a NSSet instead of a NSArray.
Use a for loop (but you don't want to iterate through the array)
Use a predicate search iUserName to see if the name exists before adding it to the array.
Something like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"iUserName == 'Giri'"];
NSArray *searchArray = [arr filterUsingPredicate:predicate];