-4

I'm trying to make a quiz app and I want the user to click a button and the program to show a random question. I tried using arc4random but it did repeat the questions, I want a truly random method. So in this forum someone told me to use a shuffle method. I tried :

-(IBAction)NextQuestion:(id)sender{

NSDictionary * questions = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"questionone", Question.text = [NSString stringWithFormat:@"xxxxxxxxxxxxxxxx?"],
                           Right2.hidden = NO,
                           Wrong3.hidden = NO,
                           Wrong1.hidden = NO,
                           Answer1.text = [NSString stringWithFormat:@"xxxx"],
                           Answer2.text = [NSString stringWithFormat:@"xxxx"],
                           Answer3.text = [NSString stringWithFormat:@"xxxx"],
                           @"questiontwo", Question.text = [NSString stringWithFormat:@"xxxxxxxxxxxxxxxxxxxx?"],
                           Right1.hidden = NO,
                           Wrong2hidden = NO,
                           Wrong3.hidden = NO,
                           Answer1.text = [NSString stringWithFormat:@"xxxx"],
                           Answer2.text = [NSString stringWithFormat:@"xxxx"],
                           Answer3.text = [NSString stringWithFormat:@"xxxx"],
                            nil];

NSMutableArray *question;
question = [NSMutableArray arrayWithObject:questions];



// shuffle
for (int i = (int)[question count] - 1; i > 0; --i) {
    [question exchangeObjectAtIndex: i
                  withObjectAtIndex: arc4random_uniform((uint32_t)i + 1)];
}

The thing is, no matter how many questions I ad, only one shows, (usually the last one), so what am I doing wrong? Can somebody help me please?? Thank you!!

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
Mykod
  • 587
  • 1
  • 5
  • 16
  • 2
    Why don't you make a dedicated class `Question` and then operate with objects of this class. Use `NSMutableArray` instead of `NSDictionary`. It is easy to shuffle or sort an array. A `NS(Mutable)Dictionary` can't be shuffled or sorted since it is not an ordered data structure. – Rok Jarc Nov 10 '13 at 12:07
  • possible duplicate of [True Random Xcode](http://stackoverflow.com/questions/19882080/true-random-xcode) – zaph Nov 10 '13 at 12:48
  • hey zaph it's not duplicate, I learned from that post and you gave me an useful answer but you did not explain it well, otherwise you would have the correct answer and I wouldn't need to create a new question. – Mykod Nov 10 '13 at 14:14

2 Answers2

4

If the key is only an index, do not use NSDictionary. They are not ordered by default!

// create a Question class with the properties number,text, answer1, ... etc. (need .h/.m and import it)

unsigned int maxCountOfQuestions = ...
NSMutableArray *tempArray = [NSMutableArray alloc] initWithCapacity: maxCountOfQuestions];
for (unsigned int index=0; index < maxCountOfQuestions; i++) {
    // make an designated initializer for your `Question` object
    Question *question = [[Question alloc] initWithText:@"textone" number:2 ...];
    [tempArray addObject:question];
}
NSArray *questions = [NSArray arrayFromArray:tempArray];
[questions shuffle]; // don't forget to import the category for the shuffle method

You want a NSArray, the shuffling can be done with this category:

//  NSMutableArray_Shuffling.h

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#include <Cocoa/Cocoa.h>
#endif

// This category enhances NSMutableArray by providing
// methods to randomly shuffle the elements.
@interface NSMutableArray (Shuffling)
- (void)shuffle;
@end


//  NSMutableArray_Shuffling.m

#import "NSMutableArray_Shuffling.h"

@implementation NSMutableArray (Shuffling)

- (void)shuffle
{
    NSUInteger count = [self count];
    for (NSUInteger i = 0; i < count; ++i) {
        // Select a random element between i and end of array to swap with.
        NSInteger nElements = count - i;
        NSInteger n = (arc4random() % nElements) + i;
        [self exchangeObjectAtIndex:i withObjectAtIndex:n];
    }
}

@end

If you want a random key from the NSDictionary, there is another good question about it at How do I select a random key from an NSDictionary?


Example

// main

unsigned int maxCountOfQuestions = 40u;
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:maxCountOfQuestions];
Question *question1 = [[Question alloc] initWithTxt:@"Color of Apples?" Answer:@"yellow" Answer:@"green" Answer:@"blue" Number:1];
[tempArray addObject:question1];

Question *question2 = [[Question alloc] initWithTxt:@"Color of Bananas?" Answer:@"yellow" Answer:@"green" Answer:@"blue" Number:2];
[tempArray addObject:question2];

Question *question3 = [[Question alloc] initWithTxt:@"Color of Peaches?" Answer:@"yellow" Answer:@"green" Answer:@"blue" Number:3];
[tempArray addObject:question3];

Question *question4 = [[Question alloc] initWithTxt:@"Color of Oranges?" Answer:@"yellow" Answer:@"green" Answer:@"blue" Number:4];
[tempArray addObject:question4];

NSArray *questions = [NSArray arrayWithArray:tempArray];
[questions shuffle]; // don't forget to import the category for the shuffle method

// you can change the questions with changing only one property
questions[3].answer1 = @"purple";

// Questions.h
#import <Foundation/Foundation.h>

@interface Question : NSObject

@property (nonatomic, readwrite, strong) NSString *questionText;
@property (nonatomic, readwrite, strong) NSString *answer1;
@property (nonatomic, readwrite, strong) NSString *answer2;
@property (nonatomic, readwrite, strong) NSString *answer3;
@property (nonatomic, readwrite, unsafe_unretained) unsigned int number;

- (id)initWithTxt:(NSString *)txt Answer:(NSString *)a1 Answer:(NSString *)a2 Answer:(NSString *)a3 Number:(unsigned int)num;

@end

// Questions.m
#import "Question.h"

@implementation Question

@synthesize questionText = _questionText;
@synthesize answer1 = _answer1;
@synthesize answer2 = _answer2;
@synthesize answer3 = _answer3;
@synthesize number = _number;

//designated initializer
- (id)initWithTxt:(NSString *)txt Answer:(NSString *)a1 Answer:(NSString *)a2 Answer:(NSString *)a3 Number:(unsigned int)num
{
    self = [super init];
    if (self != nil) {
        _questionText = txt;
        _answer1 = a1;
        _answer2 = a2;
        _answer3 = a3;
        _number = num;
    }

    return self;
}

@end
Community
  • 1
  • 1
Binarian
  • 12,296
  • 8
  • 53
  • 84
  • Thank you for your excellent answer (and most likely the correct one). When you say "create a Question class with the properties number,text, answer1..." you mean that I should create a new class and put there all my questions? like : Question.text = [NSString stringWithFormat:@"xxxxxxxxxxxxxxxx?"], Right2.hidden = NO, Wrong3.hidden = NO, Wrong1.hidden = NO, Answer1.text = [NSString stringWithFormat:@"xxxx"](...) all this? And also, what do you mean by the property number? Thank you! – Mykod Nov 10 '13 at 14:19
  • @Mykod Added an example. If the questions will not change you could init the questions with static `NSArray`s. If they are changing it would be best to implement the CoreData Database with a Question being a subclass of `NSManagedObject`. – Binarian Nov 10 '13 at 17:47
4

You realize, of course, that

question = [NSMutableArray arrayWithObject:questions];` 

results in an array of only one object -- the questions dictionary -- so "shuffling" the array is useless.

If you want to shuffle something, do

NSMutableArray* keys = [[questions allKeys] mutableCopy];

and sort that, then pick elements from that array to select from the dictionary.

(But on further observation, due to the way you do initObjectsAndKeys, you will only ever get one Question into your dictionary, though it will be there twice, under two different keys.)

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • So could you explain me what should I do? I'm new to C and never worked with NSMutableArrays or even NSDictionaries, so I would need a little more detailed explanation if you are generous enough. Thanks! – Mykod Nov 10 '13 at 14:21
  • @Mykod - First off, read the [documentation](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html) -- `arrayWithObject` only adds one object to the array. But your way of initializing the dictionary is all wrong, and it's hard to know where to start. Sigh... I'll edit my answer with some info. – Hot Licks Nov 10 '13 at 15:06
  • On second thought, your initialization is even more screwed up than I thought. You either need to define a `Question` class or make each question be an inner dictionary with keys text, right, wrong1, wrong2, answer1, etc. – Hot Licks Nov 10 '13 at 15:14
  • I created a .plist with my questions and answers, then I added my shuffle and display method but it still repeats the some questions (sometimes). I don't know what am I doing wrong. – Mykod Nov 10 '13 at 15:41
  • 2
    @Mykod - Face it -- you don't have the foggiest idea what you're doing. Start with something simpler and make sure you understand what you're doing each step of the way. And while the Apple documentation sorta sucks, it's still not all that bad, so study the documentation for classes (such as NSArray and NSDictionary) before you start coding with them. – Hot Licks Nov 10 '13 at 19:15