0

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.


     }
}
1
  • 2
    You can use a NSPredicate. Commented Jul 31, 2015 at 7:42

2 Answers 2

3

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];
Sign up to request clarification or add additional context in comments.

7 Comments

The issue is that the intersection should be case sensitive. I know the user didn't speak about it, but we can imagine that may be an issue he/she didn't think about.
@Larme Good to bring up this point, which also crossed my mind. But it's not an issue at the moment.
@techman9 Well, this code gives you a list of words that appear in both arrays, and that's what you wanted. I've added one more line that places the result back in an array.
@techman9 Please note that this answer you accepted has the sets the wrong way around; intersectionSet should be created with WORDS, not Allitems, because now every time you create a new set of the larger Allitems only to discard most (or all) of them immediately in intersectSet. Create a global set of all items and make intersectionSet from WORDS (see my answer). This may not seem important with your given (very small) set of items, but it's a very simple fix and would make a huge diference if Allitems was, say, millions of items. Best learn the correct pattern immediately.
@meaning-matters Please consider editing the answer to switch places of of the sets, so that the NSMutableSet is created out of WORDS instead of Allitems. (Consider if Allitems was a million items; then every time this matching was done you'd create insert all million of them into a new set, then lose all but 0-2 of them in intersectSet. Meanwhile if you create the NSMutableSet with WORDS, it is only 0-2 items inserted and possibly removed. It's a difference of some 2 million insertions/removals.)
|
1

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.