I have a NSMutableArray of N Integer elements (N>4), I want to get 3 different random elements from this array. I do not really need a perfectly-uniform distribution, just 3 different random elements should be OK. Do you have any suggestion? Thanks
Make NSIndexSet, and keep adding
int value = arc4random() % array.count;
items to it until its size gets to 3. The you know that you have your three indexes.
NSMutableIndexSet *picks = [NSMutableIndexSet indexSet];
do {
[picks addIndex:arc4random() % array.count];
} while (picks.count != 3);
[picks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"Element at index %ud: %#", idx, [array elementAt:idx]);
}];
for (int i = 1; i <= 3; i++) {
int index = (int)(arc4random() % [array count]);
id object = [array objectAtIndex:index];
[array removeObjectAtIndex:index];
}
arc4random() returns a random number in the range [0,2^32-1). The remainder when you take the modulus with the size of the array gets you a value between [0,arrayCountLessOne].
If you don't want to change your original data array, you can just make a copy of the array.
If you want to do this more than once from various places in your code then consider doing this: The "Objective C way" is to create a category on NSMutableArray which adds a method randomObjects. The method itself should generate three random integers from 0 to the length of the array -1 (N-1), then return a set of objects from the array at those indices, as per the other answers here (dasblinkenlight's in particular.)
First, create the category. Create a new header file NSMutableArray+RandomObject.h, containing:
#interface NSMutableArray (RandomObjects)
- (NSSet *) randomObjects;
#end
The RandomElement in parentheses is the name of your category. Any class you write that includes this new header file will give all your NSMutableArray instances the randomElement method.
Then the implementation, in NSMutableArray+RandomObjects.m:
#implementation NSMutableArray (RandomObjects)
- (NSSet *) randomObjects {
// Use the code from #dasblinkenlight's answer here, adding the following line:
return picks;
}
#end
And that's basically it. You've effectively added that capability to NSMutableArray.
Nice answer from dasblinkenlight!
In Swift 4:
let indexes = NSMutableIndexSet()
if array.count > setSize {
repeat {
indexes.add(Int(arc4random_uniform(UInt32(array.count))))
} while (indexes.count < setSize)
}
Related
I am working on a project where I am adding objects to an array when the user taps the object in my UITableView. The user can add multiples of a single object, but no more than 4. I can't figure out how to stop the object from being added if there are already 4 in the array. Is there a way to check how many of a single object are stored in the array?
Here's what I have so far:
if (![deckArray containsObject:[cardArray objectAtIndex: indexPath.row]] || [deckArray containsObject:[cardArray objectAtIndex: indexPath.row]] ) {
[deckArray addObject:[cardArray objectAtIndex: indexPath.row]];
}
First, your conditional statement is always true. It says: "if the deck does or does not contain the object then add it."
Here is an inefficient way to do this is. It is slow because you need to enumerate every card in the deckArray, but if that array will never be very large (say 100 elements), then this is good enough:
id card = [cardArray objectAtIndex:indexPath.row];
int count = 0;
for( id deckCard in deckArray )
{
if( [deckCard isEqualTo:card] )
count++;
}
if( count < 4 )
[deckArray addObject:card];
If the order of deckArray does not matter, you could use NSCountedSet and have a very quick way to check how many times you've added the card:
// somewhere else, deck is declared as:
NSCountedSet * deck = [NSCountedSet set];
// somewhere else
id card = [cardArray objectAtIndex:indexPath.row];
if( [deck countForObject:card] < 4 )
[deck addObject:card];
This question already has answers here:
Removing object from NSMutableArray
(5 answers)
Closed 7 years ago.
My program has a NSMutable Array named as "matchedCards", and I have added few object in it of type Card, now I need to remove the objects from the array, and I use the following code for it:
for (Card * removeCards in matchedCards)
{
[self.matchedCards removeObject:removeCards];
}
The first card-object gets removed , and after that the program gets crashed , Can anyone explain the reason behind it, if it successfully removes the first object, why it starts throwing error from 2nd object onwards
You can't remove elements from an array while fast-enumerating it.
If you simply want to remove all objects do
[self.matchedCards removeAllObjects];
If you want to remove only certain elements however, remember their indices in an IndexSet and remove those
NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet new];
for (NSUInteger index = 0; index < [self.matchedCards count]; ++index) {
if (whatever) {
[indexesToRemove addObject:index];
}
}
[self.matchedCards removeObjectsAtIndexes:indexesToRemove];
You can't remove an object from an array while iterating on it. Do this instead:
for (Card * removeCards in [matchedCards copy])
{
[self.matchedCards removeObject:removeCards];
}
Read the crash log. It will say something along the lines of...
"Collection was stated while being enumerated"
Or something like that.
You can't mutate an array while iterating over it using a for:in loop.
You can do this though...
[matchedCards enumerateObjectsUsingBlock:^(Card *removedCards, NSInteger idx, BOOL *stop) {
[self.matchedCards removeObject:card];
}];
Also, with your current code you are actually removing all of the objects from the matchedCards array. It will result in an empty array. Are you sure that's what you want?
The reason is because you are removing the current object and ruin the for-statement
Here's a solution:
for (int i = 0; i < self.matchedCards.count; i++)
{
if ([self.matchedCards[i] isKindOfClass:[YourClass class]])
{
[self.matchedCards removeObject:self.matchedCards[i]];
i--; // invalidate the removed index
}
}
NSLog(#"%#", self.matchedCards);
Take note that i-- is important, else you will not get through to the last element of the array..
Hope this helps you.. Cheers..
// if remove all objects
[matchedCards removeAllObjects];
// if you want to remove using index
for (int i =[matchedCards count]-1; i>=0; i++) {
if (condition) {
[matchedCards removeObjectAtIndex:i];
}
}
I'm trying to add every other object from one NSMutableArray to another.
Here is my code so far.
for (int i = 0; i < all.count; i ++) {
[cat insertObject:all atIndex:0];
[all removeObjectAtIndex:i];
}
It did not add anything from the mutableArray called all, to the mutableArray called cat.
I would like to remove all even objects from one array and put it to the next to the next.
You are inserting the array all into cat. You should be taking all's element instead, like this:
for (int i = 0; i < all.count; i += 2) {
[cat addObject:[all objectAtIndex:i]];
}
or use the square bracket syntax for accessing array elements of all, like this:
for (int i = 0; i < all.count; i += 2) {
[cat addObject:all[i]];
}
First of all you need to be sure that your cat object has been previously initialized with an empty NSMutableArray.
Then the code for passing every other object from one to another could look like:
for (int i = 0; i < [all count]; i++) {
id item = [all objectAtIndex:i];
[cat addObject:item];
[all removeObject:item];
}
This would iterate from 0 to all.count, add the object at an index to the cat array and delete it from the all array. You don't need to iterate with i += 2 because the way the removeObject works, it shifts the indexes of the remaining objects to fill the space of the object removed.
Let's say you removed the index 0, then the index 1 will be the new index 0, so for you to go to your original index 2 you will only need to go to your new index 1.
You can initialize the cat array in one shot like this
NSArray* cat =[NSArray arrayWithArray:all];
Here is a class for a deck of cards that could have several different configurations that I define using a NSDictionary with string keys and array values of how the cards are to be added. I haven't completed the init function yet, but it gives me the error above on trying to access my NSDictionary property. Fairly new to objective-c sorry if this is trivial question.
Here is my .m class file:
#interface MarioCardDeck()
#property (strong, nonatomic)NSDictionary *cardConfigurations;
#end
#implementation MarioCardDeck
- (instancetype)init {
self = [super init];
if(self) {
unsigned index = arc4random() % [[cardConfigurations allKeys] count]; ** error line
}
return self;
}
- (NSDictionary *)cardConfigurations
{
if(!_cardConfigurations)
{
_cardConfigurations = #{
#"1" :
#[#"flower",#"coin20",#"mushroom",#"star",#"oneUp",#"flower",#"oneUp",#"flower",#"coin10",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"mushroom",#"flower",#"star"],
#"2" :
#[#"flower",#"coin10",#"oneUp",#"flower",#"oneUp",#"mushroom",#"star",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"flower",#"coin20",#"mushroom",#"flower",#"star"]
};
}
return _cardConfigurations;
}
#end
You need:
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
You need to access the property by using self.
FYI - you should use:
unsigned index = arc4random_uniform([[self.cardConfigurations allKeys] count]);
You need to refer to it by self.cardConfigurations.
You need to change
unsigned index = arc4random() % [[cardConfigurations allKeys] count];
to
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
Accessing properties in the init method is, however, dangerous thing to do in Objective-C. If the getter is overridden in a subclass you might get a nasty surprise. I would make another property for the index, assign the instance variable to NSNotFound, and do the calculation the first the time the getter method is called. Also, you should use NSUInteger as the type for storing the index.
so I'm creating a contact list app and I'm storing everything into an NSMutableArray. I've managed to get add function working perfectly. But I'm struggling with the Previous/Next functions.
I can get it to go back one object in the array when pressing previous, but i Can't get it to go back any further? here's my code: I have two extra classes, PhoneBookEntry which is a subclass of the Person class. These classes contain three strings, Firstname,lastname and studentID
- (IBAction)addPerson:(id)sender {
PhonebookEntry *person = [[PhonebookEntry alloc] init];
NSLog(#"%#",self.firstName.text);
person.firstName = self.firstName.text;
person.lastName = self.lastName.text;
person.phoneNumber = self.phoneNumber.text;
[self.entries addObject:person];
Here's my Previous button:
int length;
length = [self.entries count];
if (length > 0) {
int index;
index = [self.entries count] - 1;
NSLog (#"%d the index is", index);
NSLog(#"object %#", [self.entries objectAtIndex:index]);
} else {
NSLog (#"No contacts have been entered. No");
}
//NSLog(#"%d is the length - 1 hopefully", length);
//NSLog (#"index at %d is ", length);
I've tried removing the - 1 here:
index = [self.entries count] - 1;
and changing then on the next line putting index--; but nothing seems to work. it just goes back once.
I understand that length is getting the amount of objects in the index, and then -1 but shouldnt i-- at the end of the count / - 1 keep removing it everytime its pressed??
Any ideas? Cheers
You're going to keep hitting the same index with that piece of code - you need to store the state somewhere. Who is going to hold onto what the current index is? With a good designed system the model should really take care of this.
You are best off storing the current index into an ivar and updating that every time the button is pressed.
#interface OKAClass ()
#property (nonatomic, assign) NSInteger currentIndex;
#end
//... initialise the property with a default value
self.currentIndex = [self.entries count] - 1;
//... when the button is pressed decrement the index (you might want some min / max validation or use modulus to loop over and start from the top again)
NSLog(#"%#", self.entries[self.currentIndex--]);