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];
Related
I have a simple question regarding xcode coding but don't know why things are not performing as I think. I have an array of objects (custom objects). I just want to check if this one is within the array. I used the following code:
NSArray *collection = [[NSArray alloc] initWithObjects:A, B, C, nil]; //A, B, C are custom "Item" objects
Item *tempItem = [[Item alloc] initWithLength:1 width:2 height:3]; //3 instance variables in "Item" objects
if([collection containsObject:tempItem]) {
NSLog(#"collection contains this item");
}
I suppose the above checking will give me a positive result but it's not. Further, I checked whether the objects created are the same.
NSLog(#"L:%i W:%i H:%i", itemToCheck.length, itemToCheck.width, itemToCheck.height);
for (int i = 0, i < [collection count], i++) {
Item *itemInArray = [collection objectAtIndex:i];
NSLog(#"collection contains L:%i W:%i H:%i", itemInArray.length, itemInArray.width, itemInArrayheight);
}
In the console, this is what I got:
L:1 W:2 H:3
collection contains L:0 W:0 H:0
collection contains L:1 W:2 H:3
collection contains L:6 W:8 H:2
Obviously the tempItem is inside the collection array but nothing shows up when I use containsObject: to check it. Could anyone give me some direction which part I am wrong? Thanks a lot!
The documentation for [NSArray containsObject:] says:
This method determines whether
anObject is present in the receiver by
sending an isEqual: message to each of
the receiverโs objects (and passing
anObject as the parameter to each
isEqual: message).
The problem is that you are comparing references to objects rather than the values of the objects. To make this specific example work, you will either need to send [collection containsObject:] an instance of a variable it contains (e.g. A, B, or C), or you will need to override the [NSObject isEqual:] method in your Item class.
This is what your isEqual method might look like:
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
if (self.length != other.length || self.width != other.width || self.height != other.height)
return NO;
return YES;
}
For a better implementation, you may want to look at this question.
I created a emoji menu where users can choose them and use on top of pictures. However, it has been quite hard to get the "recently chosen emoji" part working properly because NSUserDefaults probably. When I choose an emoji in any of the 8 views, this emoji is added to an NSMutableArray, and the next time I open the emoji menu, this emoji is the first to appear in the "recently chosen emoji" view, as expected. I implemented the following code so that when I choose an emoji (be it directly through the "recently chosen emoji view" or just through any of the 8 other views - the 9 views are connected through a horizontal scroll view - if this emoji is already present at the "recently chosen emoji view", it is deleted from the view and added to the beginning of the view). Actually, I delete from the index in the emoji array, remove all subviews from the "recently chosen emoji" view and repopulate it with subviews according to the new emoji array:
- (void)emojiButtonPressed:(UIButton *)button {
NSLog(#"ARRAY EQUALS = %#", historicEmojiQueue);
NSLog(#"ARRAY AMOUNT EQUALS = %lu", (unsigned long)[historicEmojiQueue count]);
if ([historicEmojiQueue count] < 30){
NSLog(#"Not full");
for ( int j = 0; j < [historicEmojiQueue count]; j++){
NSLog(#"IN THE LOOP - START PRINT");
NSLog(#"%#", button.titleLabel.text);
NSLog(#"%#", historicEmojiQueue[j]);
NSLog(#"IN THE LOOP - END PRINT");
if ( button.titleLabel.text == historicEmojiQueue[j] ){
NSLog(#"EQUAL ADDITION TO ITEM ALREADY PRESENT");
[historicEmojiQueue removeObjectAtIndex:j];
}
}
[historicEmojiQueue insertObject:button.titleLabel.text atIndex:0];
}
else{
NSLog(#"Full");
BOOL equal = NO;
for ( int j = 0; j < [historicEmojiQueue count]; j++){
NSLog(#"IN THE LOOP - START PRINT");
NSLog(#"%#", button.titleLabel.text);
NSLog(#"%#", historicEmojiQueue[j]);
NSLog(#"IN THE LOOP - END PRINT");
if ( button.titleLabel.text == historicEmojiQueue[j] ){
NSLog(#"EQUAL ADDITION TO ITEM ALREADY PRESENT");
[historicEmojiQueue removeObjectAtIndex:j];
equal = YES;
}
}
if (equal == NO){
[historicEmojiQueue removeLastObject];
}
[historicEmojiQueue insertObject:button.titleLabel.text atIndex:0];
}
NSLog(#"ARRAY EQUALS aF= %#", historicEmojiQueue);
NSLog(#"ARRAY AMOUNT EQUALS aF= %lu", (unsigned long)[historicEmojiQueue count]);
[[NSUserDefaults standardUserDefaults] setObject:historicEmojiQueue forKey:#"historicQueue"];
[[NSUserDefaults standardUserDefaults] synchronize];
(MORE CODE THAT DOES NOT AFFECT THE PROBLEM)
}
When I choose an emoji, lets say Pizza, then choose another 5 emoji that are different, and choose Pizza again, it works perfectly: Pizza is removed from the sixth position and added to the first.
If I run the app again, my last changes are saved to NSUserDefaults, so Pizza will be the first emoji to display on the "recently chosen emoji" view. Now, if I choose Pizza again, the old Pizza and first on the list is not removed, and the new one is added, then having two Pizza emoji on the first and second position of the array, and then, in the view.
If I tap on any of the old emojis in the view ( like the second pizza), it is removed and added to the beginning. In the case of the Pizza, tapping on the old Pizza (second position) will not produce any visible changes since they are equal, but I am sure they exchange places. Here is the code in ViewDidLoad to get the array from NSUserDefaults:
NSMutableArray * historicEmojiQueue;
#implementation myEmojiView
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
NSMutableArray * permanentQueue = [[NSUserDefaults standardUserDefaults] objectForKey: #"historicQueue"];
if ( permanentQueue == NULL) {
historicEmojiQueue = [[NSMutableArray alloc] init];
[[NSUserDefaults standardUserDefaults] setObject:historicEmojiQueue forKey:#"historicQueue"];
}
else{
historicEmojiQueue = [[NSMutableArray alloc] initWithArray:permanentQueue copyItems:YES];
}
}
(MORE CODE)
}
The reason of my question is the result of the print of the first block of code:
ARRAY EQUALS = (
"\Ud83c\Udf55",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83c\Udf52",
"\Ud83d\Ude18",
"\Ud83d\Ude43",
"\Ud83c\Udfc8",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83d\Ude44",
"\Ud83d\Ude0e",
"\Ud83e\Udd14",
"\Ud83d\Ude12",
"\Ud83d\Ude07",
"\Ud83d\Ude1c",
"\Ud83e\Udd11",
"\Ud83d\Ude17",
"\Ud83d\Ude18",
"\Ud83c\Udf55",
"\Ud83c\Udf2d",
"\Ud83c\Udf56",
"\Ud83c\Udf4f",
"\Ud83c\Udf50",
"\Ud83c\Udf4b",
"\Ud83c\Udf4a",
"\Ud83d\Udc2f",
"\Ud83c\Udf46",
"\Ud83d\Udc3d",
"\Ud83d\Ude4a"
)
ARRAY AMOUNT EQUALS = 30
Full
IN THE LOOP - START PRINT
๐
๐
IN THE LOOP - END PRINT
IN THE LOOP - START PRINT
๐
๐
IN THE LOOP - END PRINT
.... ( LOTS OF OTHER PRINT STATEMENTS INSIDE LOOP)
ARRAY EQUALS aF= (
"\Ud83c\Udf55",
"\Ud83c\Udf55",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83c\Udf52",
"\Ud83d\Ude18",
"\Ud83d\Ude43",
"\Ud83c\Udfc8",
"\Ud83c\Udf4e",
"\Ud83c\Udf4e",
"\Ud83d\Ude44",
"\Ud83d\Ude0e",
"\Ud83e\Udd14",
"\Ud83d\Ude12",
"\Ud83d\Ude07",
"\Ud83d\Ude1c",
"\Ud83e\Udd11",
"\Ud83d\Ude17",
"\Ud83d\Ude18",
"\Ud83c\Udf55",
"\Ud83c\Udf2d",
"\Ud83c\Udf56",
"\Ud83c\Udf4f",
"\Ud83c\Udf50",
"\Ud83c\Udf4b",
"\Ud83c\Udf4a",
"\Ud83d\Udc2f",
"\Ud83c\Udf46",
"\Ud83d\Udc3d"
)
ARRAY AMOUNT EQUALS aF= 30
As you can see, even though the first emoji is exactly equal to the second both printed and in their unicode strings, they do not pass the check of whether they are equal as if they had different string representations. Since when I do not run the application again from Xcode and choose two equal emojis they do pass the check and print "EQUAL ADDITION TO ITEM ALREADY PRESENT", I am sure the problem relates to data being retrieved from NSUserDefaults, even though when I print the array I got from NSUserDefaults, it is exactly the same. I've tried multiple things when copying array from NSUserDefaults like [[NSMutableArray alloc] initWithArray:permanentQueue copyItems:YES]; and even looping through the NSUserDefaults array copying each item but nothing seems to solve the problem. Please help! :(
Where you are comparing strings at
if ( button.titleLabel.text == historicEmojiQueue[j] )
you should be using
if ( [button.titleLabel.text isEqualToString:historicEmojiQueue[j]] )
The first compares whether they occupy the same memory, the second whether they match.
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];
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)
}