简体   繁体   中英

Check if NSMutableArray contains an int

I am making an app which asks the user a series of questions. The question asked depends on the random int produced. When an int is used, I want to add it to an NSMutableArray , and then check if the array contains a number the next time a random number is chosen. I am currently using the following code to do this:

- (void) selectQuestionNumber {
textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);

if ([previousQuestions containsObject:[NSNumber numberWithInt:textNum]]) {
    [self selectQuestionNumber];
    NSLog(@"The same question number appeared!");
} else {
    questionLabel.text = [self nextQuestion];
    [self questionTitleChange];

    NSLog(@"New question made");
}
[previousQuestions addObject:[NSNumber numberWithInt:textNum]];
}  

However, the code NSLog(@"The same question number appeared!"); is never shown in the console, even when the same question will appear twice.

My code is obviously non-functional, so what code can I use to check if an NSMutable array contains an int ?

Your problem is likely something other than detecting membership of NSNumbers in an NSArray. It can take a large number of tries for a set of random numbers to repeat. It is theoretically possible for it to not repeat until every possible value has been generated once. For a large range of legal values it can take quite a while.

I suggest logging the values that you add to the array on each pass, and the new value.

Your code above always adds the new value to the array even it if matched, so your array is going to grow with duplicates. You would be better off only adding the new number to the array if it did not match. you would probably also be better off using an NSMutableSet instead of an array. NSSets contain at most one instance of an object, and their containsObject method is faster than that of NSArray.

Original solution (works with Array and Set):

-(void)selectQuestionNumber
 {
   textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);

    NSPredicate *predicate =  [NSPredicate predicateWithFormat:@"intValue=%i",textNum];
    NSArray *filteredArray = [previousQuestions filteredArrayUsingPredicate:predicate];

    if ([filteredArray count]) {
        [self selectQuestionNumber];
        NSLog(@"The same question number appeared!");
    } else {
        questionLabel.text = [self nextQuestion];
        [self questionTitleChange];

        NSLog(@"New question made");
    }
    [previousQuestions addObject:[NSNumber numberWithInt:textNum]];
}

Best solution, and better performance, especialy with mutableSet ( According to Duncan C).

 -(void)selectQuestionNumber
{

textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);

if ([previousQuestions containsObject:[NSNumber numberWithInteger:textNum]]) {
    [self selectQuestionNumber];
    NSLog(@"The same question number appeared!");
} else {
    questionLabel.text = [self nextQuestion];
    [self questionTitleChange];

    NSLog(@"New question made");
    // And add the new number to mutableSet of mutableArray.
    [previousQuestions addObject:[NSNumber numberWithInteger:textNum]];
}

}

Instead of using NSArray, you can use NSMutableIndexSet. This is the same as NSSet, with just NSUIntegers instead of objects. Very useful.

//during init
NSMutableIndexSet *tSet = [[NSMutableIndexSet alloc] init];
//...
//later in the code, in whatever loop you have on new values
NSUInteger newInt = lowerBounds + arc4random() % (upperBounds - lowerBounds);
if ([tSet containsIndex:newInt]){
    //value already exists in the set
}
else {
    //value does not exist, add it
    [tSet addIndex:newInt];
}
NSMutableSet *myNumbers = [NSMutableSet Set]; // or NSMutableArray..


NSNumber *aNumber = [NSNumber numberWithInt:getRandomInt() ]; //let us say it returns 1.

[myNumbers addObject:aNumber];



    -(BOOL)succesfullyAddNewUniqueRandomMember{

        NSInteger randInt = getRandomInt(); //let us say it returns 1 again..

        NSNumber *aSubsequentNumber = [NSNumber numberWithInt:randInt;

 for (NSNumber *previousEntry in myNumbers){


  if ([previousEntry isEqual:aSubsequentNumber]) return NO;

  }
[myNumbers addObject:aSubsequentNumber];
 return YES;

}

^ are these objects equal (aNumber, aSubsequentNumber) ? YES

^ are they the same object ? NO, two different NSNumbers made with equal integer..

NSSet will also happily add both, because they are not the same object.

therefore you need to loop through and compare directly to each previous member, the (already contains object) filter of NSSet will not do the trick.

by wrapping this in a -(BOOL) type method we can repeat it with a

while(![self succesfullyAddNewUniqueRandomMember])

In other words, in your code

if ([previousQuestions containsObject:[NSNumber numberWithInt:textNum]])

always returns NO because it is comparing NSNumber objects, not their integerValue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM