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]];
}
Related
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?
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)];
}
I have an array of search values that I would like to use in conjunction with a LIKE query in an NSPredicate:
NSPredicate *p = [NSPredicate predicateWithFormat:#"textField IN LIKE[c] %#", array];
This doesn't work, but is this possible to see if textfield has any LIKE comparisons to each value in the array?
You should use NSCompoundPredicate. It let's you combine a series of LIKE predicates with OR.
NSMutableArray *subpredicates = [NSMutableArray array];
for(NSString *text in array)
[subpredicates addObject:[NSPredicate predicateWithFormat:#"textfield LIKE %#",text]];
NSPredicate *finished = [NSCompoundPredicate orPredicateWithSubpredicates:subpreds];//Your final predicate
I have got a NSMutableArray that is structured as follow:
{
{
AccountNumber:Test1
Type: Electricity
}
{
AccountNumber:Test2
Type: Water
}
{
AccountNumber:Test3
Type: Water
}
}
How to print out the account number that are in Water type?
What I have tried is as follow:
- (NSUInteger)indexOfObjectIdenticalTo:(id)data{ return data; }
But I didn't understand how to do it.
Check out NSPredicate. It will allow to you essentially define a query and apply it to your array to filter the results. This is much faster than a iterative loop through the array to find what you are looking for. For your example, you would simply need to do the following:
NSString *type = #"Water";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Type == %#", type];
NSArray *results = [myArray filteredArrayUsingPredicate:predicate];
Assuming each item in the array is a NSDictionary, and all the keys/values are NSStrings, you could do something like this:
for (NSDictionary *dict in myArray) {
if ([[dict objectForKey:#"Type"] isEqualToString:#"Water"]) {
NSLog(#"Account number %#", [dict objectForKey:#"AccountNumber"]);
}
}
I have a NSMutableArray which contains a few NSString objects. How can I test if the array contains a particular string literal?
I tried [array containsObject:#"teststring"] but that doesn't work.
What you're doing should work fine. For example
NSArray *a = [NSArray arrayWithObjects:#"Foo", #"Bar", #"Baz", nil];
NSLog(#"At index %i", [a indexOfObject:#"Bar"]);
Correctly logs "At index 1" for me. Two possible foibles:
indexOfObject sends isEqual messages to do the comparison - you've not replaced this method in a category?
Make sure you're testing against NSNotFound for failure to locate, and not (say) 0.
[array indexOfObject:object] != NSNotFound
Comparing against string literals only works in code examples. In the real world you often need to compare against NSString* instances in e.g. an array, in which case containsObject fails because it compares against the object, not the value.
You could add a category to your implementation which extends NS(Mutable)Array with a method to check wether it contains the string (or whatever other type you need to compare against);
#implementation NSMutableArray (ContainsString)
-(BOOL) containsString:(NSString*)string
{
for (NSString* str in self) {
if ([str isEqualToString:string])
return YES;
}
return NO;
}
#end
You may also use a predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %#", theArray];
BOOL result = [predicate evaluateWithObject:theString];
for every object
[(NSString *) [array objectAtIndex:i] isEqualToString:#"teststring"];