techman9
techman9

Reputation: 79

Find string in array

I have a fun challenging problem. So I have a mutable array that contains all of my items. I have a textfield that **might have one or two of these items if the person types them in. **

items= [[NSArray alloc]initWithObjects:@"apple", @"orange", @"pear", nil];

items2= [[NSArray alloc]initWithObjects:@"cheese", @"milk", @"eggs", nil];

Allitems= [NSMutableArray array];
[Allitems addObjectsFromArray:items];
[Allitems addObjectsFromArray:items2];

NSArray*WORDS =[Textfield componentsSeparatedByString:@" "];

I am trying to detect what specific words from **Allitems are in the textfield. (If the textfield contains any string from ALLitems, how can I find what specific string?**

for (int i = 0; i < [Allitems count]; i++)
{
      NSString *grabstring;
      grabstring=[Allitems objectAtIndex:i];

      if (textfield isEqualto:grabstring){
        ?????
           pull that specific string from allitems.


     }
}

Upvotes: 0

Views: 80

Answers (2)

meaning-matters
meaning-matters

Reputation: 23006

You want the intersection of two sets:

NSMutableSet* intersectionSet = [NSMutableSet setWithArray:Allitems];
[intersectionSet intersectSet:[NSSet setWithArray:WORDS]];
NSArray* intersectionArray = [intersectionSet allObjects];

After this intersectionArray contains the items that are present in both Allitems and WORDS.

BTW, why do you capitalise variable names in a non-standard and inconsistent manner? Why not just allItems and words?

As @Arkku suggests: It's better to switch the arrays. In your example it does not matter much, but in case Allitems were (very) big, you can save (a lot of) memory and CPU usage:

NSMutableSet* intersectionSet = [NSMutableSet setWithArray:WORDS];
[intersectionSet intersectSet:[NSSet setWithArray:Allitems]];
NSArray* intersectionArray = [intersectionSet allObjects];

Upvotes: 3

Arkku
Arkku

Reputation: 42159

There are a various ways of doing it, each with different pros and cons. Let's have the following (consistently capitalized) variables in common for each case:

NSArray *allItems = @[ @"apple", @"orange", @"pear", @"cheese", @"milk", @"egg" ];
NSString *textFieldText = @"CHEESE ham pear";
NSArray *words = [textFieldText.lowercaseString componentsSeparatedByString:@" "];

NSPredicate

NSArray *matchingItems = [allItems filteredArrayUsingPredicate:
                          [NSPredicate predicateWithFormat:@"SELF IN %@", words]];

This is perhaps the shortest (in lines of code) way, but not the most performant if allItems can be very long as it requires traversing all of it.

Iteration

Of course you could also simply iterate over the collection and do the matching manually:

NSMutableArray *matchingItems = [NSMutableArray array];
for (NSString *item in allItems) {
    if ([words containsObject:item]) {
        [matchingItems addObject:item];
    }
}

Again requires traversing all of allItems (although you could break the iteration if all words are matched).

In addition to the for loop there are of course many other ways for iteration, e.g., enumerateObjectsUsingBlock:, but they are unlikely to have any advantage here.

NSSet

NSSet is often a good option for this kind of matching since testing set membership is faster than with NSArray. However, if using the most straightforward method intersetSet: (in NSMutableSet) care must be taken to not inadvertently create a large mutable set only to discard most of its items.

If the order of allItems does not matter, the best way would be to change it from an array into a set and always keep that set around, i.e., instead of creating the array allItems, you would create an NSSet:

NSSet *setOfAllItems = [NSSet setWithArray:allItems];

Or if it needs to be mutable:

NSMutableSet *setOfAllItems = [NSMutableSet set];
[setOfAllItems addObjectsFromArray:items1];
[setOfAllItems addObjectsFromArray:items2];

Then, when you have that set, you create a temporary mutable set out of words (which is presumably always the smaller set):

NSMutableSet *setOfMatches = [NSMutableSet setWithArray:words];
[setOfMatches intersectSet:setOfAllItems];
NSArray *matchingItems = setOfMatches.allObjects;

This would be likely be the most performant solution if setOfAllItems is large, but note that the matches will then need to be exact. The other methods are more easily adapted to things like matching the strings in words against fields of objects or keys in a dictionary (and returning the matched objects rather than the strings). In such a case one possibility to consider would be an NSDictionary mapping the words to match to the objects to return (also fast to then iterate over words and test for membership in the dictionary).

Conversion to string

And, since the question included conversion of matches to a string:

[matchingItems componentsJoinedByString:@", "]

In the example case this would result in the string "pear, cheese" (or possibly "cheese, pear" if using sets).

Upvotes: 1

Related Questions