tags:

views:

42

answers:

2

I'm trying to make an array of objects in random, non repeating order. Using NSMutableSet for the heavy lifting as recommended here: http://stackoverflow.com/questions/2141121/how-to-check-repetition-of-numbers-in-an-array

I'm dumping them into an NSArray after creation to access them, but the NSArray doesn't stay in the order I placed them in the NSMutableSet. Making it more confusing is that it's not consistent.

My code:

NSArray *puzzleImages = [[NSArray alloc] initWithObjects:@"elephant.png", @"gorilla.png", @"lion.png", @"zebra.png", @"flamingo.png", @"hyena.png", @"seal.png", @"hippo.png", @"rhino.png", @"tiger.png", @"macaw.png", @"bear.png", nil];

NSArray *puzzleSounds = [[NSArray alloc] initWithObjects:@"elephant", @"gorilla", @"lion", @"zebra", @"flamingo", @"hyena", @"seal", @"hippo", @"rhino", @"tiger", @"macaw", @"bear", nil];

NSMutableSet *aImages1 = [NSMutableSet setWithCapacity:2];
NSMutableSet *aSounds1 = [NSMutableSet setWithCapacity:2];
NSMutableSet *aValues1 = [NSMutableSet setWithCapacity:2];
while([aImages1 count]<=1){
  int Randnum1 = arc4random() % 11;
  [aImages1 addObject:[puzzleImages objectAtIndex:Randnum1]];
  [aSounds1 addObject:[puzzleSounds objectAtIndex:Randnum1]];
  [aValues1 addObject:[NSNumber numberWithInt:Randnum1]];
  NSLog(@"image:%@, sound:%@, value:%@",[puzzleImages objectAtIndex:Randnum1],[puzzleSounds objectAtIndex:Randnum1],[NSNumber numberWithInt:Randnum1]);
} 

NSArray *arrayOfImages1 = [aImages1 allObjects];
NSLog(@"arrayOfImages1: %@",arrayOfImages1);

NSArray *arrayOfSounds1 = [aSounds1 allObjects];
NSLog(@"arrayOfSounds1: %@",arrayOfSounds1);

NSArray *arrayOfValues1 = [aValues1 allObjects];
NSLog(@"aValues1: %@",aValues1);

Here's my output:

2010-06-20 16:13:14.572 MatchGame[22675:207] image:lion.png, sound:lion, value:2
2010-06-20 16:13:14.574 MatchGame[22675:207] image:macaw.png, sound:macaw, value:10
2010-06-20 16:13:14.575 MatchGame[22675:207] arrayOfImages1: (
"lion.png",
"macaw.png")
2010-06-20 16:13:14.575 MatchGame[22675:207] arrayOfSounds1: (
macaw,
lion)
2010-06-20 16:13:14.576 MatchGame[22675:207] aValues1: {(
2,
10)}

How the heck did the Macaw sound end up above the lion sound? Course it doesn't always happen, but it breaks the game when it does. I'm sure I'm missing something silly, but spent enough time trying to figure it out on my own.

+1  A: 

Sets have no order - so if you want to keep things in the same order you added you need to use Arrays.

Also in this case if you have two items the same in your original array you will get less elements out of the set.

Mark
The reason I don't use Arrays is because of the risk of duplications. There's no built in "keep them unique" checker as there is in a set. I'm sure I could check the array for the the object before adding it, but was trying to avoid that.
Lawrence Ingraham
All you need to do is send `-containsObject:` to an array to see if it is already there. So it's not that much extra work (for you, there might be performance implications for large arrays).
JeremyP
+2  A: 

Sets are not ordered. In particular, the ordering you get out of them will depend on the objects you put in rather than the order you add them. (Often they are implemented using some kind of object hash, though I don't know offhand if that's the case here.)

Since you want to keep a bunch of items linked, one option would be to make a single object class that contains them all, and store that in the set. You can then get it out and read off its different fields.

Another option would be just to scramble one list of indices and then access the objects in your various original arrays in that order.

Update: the post you link to is solving a rather different problem from yours. In your case, using set ordering to randomise is really the wrong thing to do, because the ordering will not be actually random. The right thing to do is to scramble a single array of indices, something like this:

NSMutableArray *indices = [NSMutableArray arrayWithCapacity:11];
for ( int i = 0; i < 11; ++i )
    [indices addObject:[NSNumber numberWithInt:i]];
for ( int i = 0; i < 11; ++i )
    [indices exchangeObjectAtIndex:i withObjectAtIndex:(arc4random() % 11)];

NSLog(@"First random image: %@", [puzzleImages objectAtIndex:[[indices objectAtIndex:0] intValue]]);

And so on. Your guarantee of uniqueness comes from the fact that you start out with indices 0..10 in the list and then shuffle, rather than trying to conjure the indices themselves from a random number generator that may produce duplicates.

walkytalky
I've tried that, but couldn't figure out how to access the objects in the Set. For example, I'd create a single NSMutableSet *randomSetOfNumbers, then grab each and create an array with the object I needed.Maybe I'm going at it the wrong way? I'm also pretty new to obj-c so I may be missing something in your explanation.
Lawrence Ingraham
@Lawrence See update. Don't use a set, it's not appropriate here. Get your uniqueness guarantee from what you put in the indices list, then access the other stuff accordingly.
walkytalky
That makes sense, and the code makes sense. I guess I was trying Sets since it seemed to solve the problem fast.I'm getting an Invalid Operands to binary % error when I try running your code though.
Lawrence Ingraham
@Lawrence: Oops - I missed the function call brackets on `arc4random()`, so it was trying % a function pointer. Fixed.
walkytalky
Perfect, works like a charm. Thanks. btw: also missed the @ in the NSLog(@""); but I caught that one.
Lawrence Ingraham
Doh. Clearly asleep at the wheel tonight!
walkytalky