0

I am trying to write a function which will allow me to determine whether one NSString* contains the characters of another NSString*. As an example, refer to the below scenario:

NSString *s1 = @"going";
NSString *s2 = @"ievngcogdl";

So essentially when the comparison between these 2 strings occurs, it should return true as the first string s1 has the same characters of the second string s2. Could I use an NSCountedSet? I know that this class has a method containsObject:(id) although I don't think that will solve my problem. Is there any other ways in completing this function and provide me the required results?

Maff
  • 1,032
  • 4
  • 25
  • 42
  • For just a few cases I'd maybe take each character from the first string, put it in an NSString, and do `rangeOfString` on the second. If I had to do a bunch of searches against the second string I'd put the characters (converted to NSNumbers) in a set and search the set with one character at a time from the first string. But Droppy's way is probably a good compromise all around (though one could speed it up a hair by extracting both strings as unichar arrays before beginning). – Hot Licks Oct 17 '14 at 11:31

4 Answers4

2

I think this method could be rather slow, but I would still favour it over [NSString rangeOfCharacterFromSet:], which requires creating an NSCharacterSet object per comparison:

- (BOOL)string:(NSString *)string containsAllCharactersInString:(NSString *)charString {
    NSUInteger stringLen = [string length];
    NSUInteger charStringLen = [charString length];
    for (NSUInteger i = 0; i < charStringLen; i++) {
        unichar c = [charString characterAtIndex:i];
        BOOL found = NO;
        for (NSUInteger j = 0; j < stringLen && !found; j++)
            found = [string characterAtIndex:j] == c;
        if (!found)
            return NO;
    }
    return YES;
}
Droppy
  • 9,691
  • 1
  • 20
  • 27
  • Thanks for that. If I were to implement it using [NSString rangeOfCharacterFromSet: @], would it be faster? Even though I would be creating an instance of the object per comparison? – Maff Oct 17 '14 at 10:45
  • @Maff You'd have to time it to be sure, however creating an object is not cheap and `rangeOfCharacterFromSet:` must be iterating over the string anyway, so we may as well keep it simple and do the iteration ourself. – Droppy Oct 17 '14 at 10:46
  • Definitely agree. I will try both out though and see how I go. I will let you know the results; in terms of speed. Thanks for the help. – Maff Oct 17 '14 at 10:47
  • This solution doesn't handle the situation where the first string contains a single instance of a letter but the second string contains two instances of that letter - as in your example "ievngcogdl" and "going". It returns YES for "ievngcodl" and "going" although there is only a single g in the comparison string – Paulw11 Oct 17 '14 at 11:20
  • @Paulw11 I didn't see that as a requirement from reading the question. – Droppy Oct 17 '14 at 11:25
  • I wasn't sure - I was merely assuming based on the double g – Paulw11 Oct 17 '14 at 11:26
  • @Maff Will have to clarify. – Droppy Oct 17 '14 at 11:27
  • Yes. I will need to consider repetitive characters. – Maff Oct 17 '14 at 11:39
2

This will work -

-(BOOL) string:(NSString *)string1 containsInputString:(NSString *)string2 {

    // Build a set of characters in the string

    NSCountedSet *string1Set = [[NSCountedSet alloc]init];

    [string1 enumerateSubstringsInRange:NSMakeRange(0, string1.length)
                                options:NSStringEnumerationByComposedCharacterSequences
                             usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                                 [string1Set addObject:substring];
                             }];


    // Now iterated over string 2, removing characters from the counted set as we go
    for (int i=0;i<string2.length;i++) {
        NSRange range = [string2 rangeOfComposedCharacterSequenceAtIndex:i];
        NSString *substring = [string2 substringWithRange:range];
        if ([string1Set countForObject:substring]> 0) {
            [string1Set removeObject:substring];
        }
        else {
            return NO;
        }
    }
    return YES;
}
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Thanks Paul, I will definitely this solution also a shot! Quick question, based on the logic outlined above, it looks like the `string1` method parameter is the one which contain the longer character set? Is that correct? So from my example above it would be: `s2`? – Maff Oct 17 '14 at 10:58
  • Yes, I switched string1 and string2 to make the method name nicer :) – Paulw11 Oct 17 '14 at 11:03
  • If you use this, make sure you use my updated code - The initial version didn't initialise the counted set properly – Paulw11 Oct 17 '14 at 11:30
  • I don't understand your comment. It works for your test case in your question. What is the test case for which it fails – Paulw11 Oct 17 '14 at 23:03
  • Sorry Paul I was believe that I was calling the inbuilt method name for strings named containsString, which was only returning me exact matches. All is good now. – Maff Oct 17 '14 at 23:04
  • Yeah, I should probably change the method name to avoid a clash – Paulw11 Oct 17 '14 at 23:06
  • I don't understand why I cannot call this method in an IF statement? It does not recognize this method for some reason, even with the changed method name. – Maff Oct 17 '14 at 23:07
  • I am trying to call like this `[word1 containsInputString: anotherWord]` in an IF statement, although it does not work. Why is this? – Maff Oct 17 '14 at 23:22
  • To work that way you would need to put this method in an NSString category. If it is just a method in your class then you would call it as `[self string:word1 containsInputString: anotherWord];` – Paulw11 Oct 17 '14 at 23:50
-1

Regular Expressions are the best way to check this type of conditions and check this link once

Below I am adding the code for your solution, please check once

  NSString *s1 = @"going"
  NSString *s2 = @"ievngcogdl";

  if ([self string:s1 containsSameCharacterofString:s2]) {

            NSLog(@"YES");

  }


    - (BOOL)string:(NSString *)str containsSameCharacterofString:(NSString *)charString
    {

        if (charString.length >= str.length) {

            NSError *error = nil;
            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"^[%@]+$", charString] options:NSRegularExpressionCaseInsensitive error:&error];

            NSRange textRange = NSMakeRange(0, str.length);
            NSRange matchRange = [regex rangeOfFirstMatchInString:str options:NSMatchingReportProgress range:textRange];

            return (matchRange.location != NSNotFound);

        }
        else {

            return NO;
        }

    }
Community
  • 1
  • 1
Anjaneyulu Battula
  • 1,910
  • 16
  • 33
-3
BOOL containsString = [@"Hello" containsString:@"llo"];
if (containsString) {
    // Do Stuff
}
Rob Sanders
  • 5,197
  • 3
  • 31
  • 58
  • It comes with the latest build of the Foundation framework in Xcode 6. It works like an NSPredicate i.e. [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", string]; I thought the answer/method name is fairly self explanatory and it works, hence the +1. – Rob Sanders Oct 17 '14 at 11:41
  • This is not a method on `NSString` read the Apple Documentation (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html) Where in this documentation is this method? And because it doesn't exist this is the reason for the 4x downvotes. This answer is incorrect – Popeye Oct 17 '14 at 11:51
  • This is in NSString.h in the foundation framework: `/* containsString: returns YES if the target string is contained within the receiver. Same as calling rangeOfString:options: with no options, thus doing a case-sensitive, non-literal search. localizedCaseInsensitiveContainsString: is the case-insensitive variant. Note that it takes the current locale into effect as well. Locale-independent case-insensitive operation, and other needs can be achieved by calling rangeOfString:options:range:locale: directly. */ - (BOOL)containsString:(NSString *)aString NS_AVAILABLE(10_10, 8_0);` – Rob Sanders Oct 17 '14 at 12:08
  • If this satisfies you please could you take away the - points – Rob Sanders Oct 17 '14 at 12:21
  • 2
    Whether or not the method exists it doesn't meet the requirements of the question as they are not looking for a substring but rather for the letters in any order – Paulw11 Oct 17 '14 at 12:53