indexOfObject with Duplicate Objects - ios

I have an NSArray of songs and would like to put them in a playlist. When I play the playlist I use indexOfObject to figure out the current songs index in the queue. I already overided isEqual, so to show that two media items were the same and would prefer not to hack at this method. I tried out NSOrderedSet instead of NSArray and it displayed each song once as opposed to how many instances of the same song are in it. Is their a class that mixes NSArray and NSOrderedSet so that each index is a unique object but there can be duplicate objects at different indexes?
Code:
- (PlayerItem *)nextSong{
if(queue.count > 0){
NSInteger songIndex = [queue indexOfObject:currentSong];
NSLog(#"%ld in %lu", (long)songIndex, (unsigned long)queue.count);
songIndex = songIndex + 1;
if (songIndex < [queue count]){
return queue[songIndex];
}else{
return queue[0];
}
}
}
return nil;
}
Comment from Below:
I mean if I cycle threw songs than it can play Song 1 then 1 again
then 2 then 3 then 3 again. This isn't possible though because the
indexOfObject always returns the first duplicate object.

The problem is in the way you are finding out which song from the playlist is currently playing: You seem to store the currentSong in an ivar or so.
As it is possible that this song appears twice in the playlist it is just not enough information to know what's playing. Example:
"My playlist is A, A, B, C, C. Right now A is playing."
As you can see, from this information you can't know what's the next song.
A solution would be to store the index in the playlist or some other information to know the progress in the playlist.

Related

Can we create an array dynamically in ios?

I have a user input in my app, according to which, the number of arrays that I need to create changes (user inputs the number of channels into which I need to split an incoming data stream, with one array for each channel). The number of channels can be any multiple of 4 up to 32. I want to know whether there is a way to automatically initialize arrays, perhaps using for loops like in this pseudocode:
for(initialization, numberOfChannels, increment) {
NSString (set array name to channel%dArray, i);
NSMutableArray *channeliArray = [[NSAMutablerray alloc] init];
}
Everywhere I've looked, I didn't find any way to set the name of the array dynamically and hence allow initializations like the one above. Even using a 2D array would have the same problem. Any help is appreciated. Thank you!
Just use a two dimensional array as per #Jonah's comments -
self.channels=[NSMutableArray new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels addObject:[NSMutableArray new]];
}
Then you can access a particular channel's array by index -
NSMutableArray *channel=[self.channels objectAtIndex:channelNumber];
Or, if you wanted to use channel names as per your pseudocode, use a dictionary of arrays
self.channels=[NSMutableDictionary new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels setObject:[NSMutableArray new] forKey:[NSString stringWithFormat:#"channel%dArray",i]];
}
Then you can access a particular channel's array by name -
NSMutableArray *channelArray=[self.channels objectForKey:#"channel3Array"];

Loop an Array in order and repeat

I am creating a local multiplayer text based game that could involve up to an unlimited number of players. I have created an array to hold all the players names that are inputed by the user on the launch of the app. However I am having problems trying to work out how to display the array index by index and then repeat on its self such as
player 1 turn
player 2 turn
player 1 turn
player 2 turn
player 1 turn
And so forth until all questions have been asked (think of a quiz game)
So far I have come up with this but its not doing the job
int i;
int count;
for (i = 0, count = [_players count]; i < count; i = i + 1)
{
NSString *element = [_players objectAtIndex:i];
self.turnlabel.text =( i, element);
NSLog(#"The element at index %d in the array is: %#", i, element);
}
Previously when I was working without and array I used a simple if statement that checked the value of a string and counted how many times its been shown and then added to this and and if the value was less than the previous string the app determined it was the other players turn. However now I am working with an array I cant seem to work out how to do this.
Basically how would I go about showing each index in array in turn and then repeating until the game ends?
EDIT
The game ends when it runs out of questions that I am generating from a plist.
Each player will take it in turns so player 1 then player 2 then player 3 and then back to player 1 and player 2 and so forth. They will be asked one question at a time.
Sorry in advance if this is a very basic question I'm very very new to Xcode my first week now.
What you probably want to do is hold a reference to the current player. Then loop through the questions. If player gets them correct, continue to next question, if player gets it wrong, then increment the player reference. Do a check when you increment the player to see if it's necessary to go back to player #1.
So, for example:
NSArray *playerArray = #[#"Player 1", #"Player 2", #"Player 3"];
NSUInteger currentPlayerIndex = 1;
Player then has his turn, if he gets it incorrect,
currentPlayerIndex++;
Then check:
if (curentPlayerIndex >= playerArray.count) {
currentPlayerIndex = 0;
}
Hope this helps.

Objective-C how to pull several random items out of NSArray without getting duplicates? [duplicate]

This question already has answers here:
Getting a random object from NSArray without duplication
(3 answers)
Closed 7 years ago.
I have an array of random properties I would like to assign to equipment within the game I'm developing.
The code that I use below is returning an NSArray. I'm interested if there's way to get item indices from that array without getting duplicate values. The obvious solution is to create a mutable array with the returned array, do random, remove item that was returned and loop until the number of items is received.
But is there a different way of getting X random items from NSArray without getting duplicates?
//get possible enchantments
NSPredicate *p = [NSPredicate predicateWithFormat:#"type = %i AND grade >= %i", kEnchantmentArmor,armor.grade];
NSArray* possibleEnchantments = [[EquipmentGenerator allEnchantmentDictionary] objectForKey:#"enchantments"];
//get only applicable enchantments
NSArray *validEnchantments = [possibleEnchantments filteredArrayUsingPredicate:p];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:validEnchantments];
NSDictionary* enchantment = nil;
if(mutableArray.count>0)
{
//got enchantments, assign number and intensity based on grade
for (int i = 0; i<3;i++)
{
enchantment = mutableArray[arc4random()%mutableArray.count];
[mutableArray removeObject:enchantment];
//create enchantment from dictionary and assign to item.
}
}
You can shuffle the array using one of the following techniques:
What's the Best Way to Shuffle an NSMutableArray?
Non repeating random numbers
Then, take the first X elements from the array.
Many years ago, I was working on card game and I realized that shuffling the deck was an inefficient way to get random cards. What I would do in your shoes is pick a random element, and then replace it with the element at the end of the array, like so:
#interface NSMutableArray (pickAndShrink)
- (id) pullElementFromIndex:(int) index // pass in your random value here
{
id pickedItem = [self elementAtIndex:index];
[self replaceObjectAtIndex:index withObject:[self lastObject]];
[self removeLastObject];
return pickedItem;
}
#end
The array will shrink by one every time you pull an element this way.
You could use a random number generator to pick a starting index, and then pick the subsequent indices based on some kind of math function. You would still need to loop depending on how many properties you want.
Eg:
-(NSMutableArray*)getRandomPropertiesFromArray:(NSArray*)myArray
{
int lengthOfMyArray = myArray.count;
int startingIndex = arc4random()%lengthOfMyArray;
NSMutableArray *finalArray = [[NSMutableArray alloc]init]autorelease];
for(int i=0; i<numberOfPropertiesRequired; i++)
{
int index = [self computeIndex:i usingStartingIndex:startingIndex origninalArray:myArray];
[finalArray addObject:[myArray objectAtIndex:index]];
}
return finalArray;
}
-(int)computeIndex:(int)index usingStartingIndex:(int)startingIndex
{
//You write your custom function here. This is just an example.
//You will have to write some code to make use you don't pick an Index greater than the length of your array.
int computedIndex = startingIndex + index*2;
return startingIndex;
}
EDIT: Even your computeIndex function could use randomness in picking the subsequent indices. Since you have a startingIndex, and another index, you could use that to offset your function so that you never pick a duplicate.
EDIT: If your array is very large, and the subset you need to pick is small, then rather than shuffle the entire array (maybe more expensive), you could use this method to pick the number of items you need. But if your array is small, or if the number of items you need to pick are almost the size of the array, then the godel9's solution is better.
You can use a mutable array and then remove them as the are selected, use something like random()%array.count to get a random index. If you don't want to modify the array then copy it with [array mutableCopy].

Problems with objectAtIndex of NSMutableArray

I am developing a game for iPhone in which I have 2 arrays.
One with the objects "living" in the game, and when these objects "die" they are placed in another array to be removed from the first one in the end of the first loop.
This is necessary because if the object dies in the loop and it is removed immediately from the first array, the index is lost and sometimes gives an invalid address access.
Then I have an NSMutableArray that contains objects to be removed from the living objects array.
In the array with objects to remove, I am constantly adding and removing items. Sometimes when I use objectAtIndex with the array with items to remove, it is retuning me other object.
For example in the first moment _enemiesToRemove has 2 objects:
([0]-> 0x0a8b5120, [1]->0x18f3a090)
for(int i = [_enemiesToRemove count] - 1; i >= 0 ; i--){
fish = [_enemiesToRemove objectAtIndex:i];
[self removeEnemy:fish];
}
and then
int i = [_enemiesToRemove count] - 1
gives i=1 but when it is doing
[_enemiesToRemove objectAtIndex:i]
it is returning me other object with address 0xbfffcd58 and it should be [1]->0x18f3a090
What is happening here, somebody knows? It is not happening always. It happens randomly.

Comparing two NSDictionaries and deleting if keys don't match

In my app I'm using two NSMutableDictionaries. Lets call them basicDictionary and refreshDictionary.
In basicDictionary I have values, lets say
key:"1" value:"2000" photo:someUIImage //Lets say I want to track score of player
key:"2" value:"1500" photo:someUIImage2
key:"3" value:"1500" photo:someUIImage3
key:"4" value:"1500" photo:someUIImage4
key stands for player id and rest is pretty clear.
I do server api call every 20 sec to refresh score of players. Because I don't want to load photo as well im using refresh call to bring me only player id and his actual score.
So every 20 secs I get data that I save in refreshDictionary. There are only ids and current score of that id.
refreshDictionary example:
key:"1" value:"2500"
key:"2" value:"2800"
key:"3" value:"2700"
I update my tableView with new values. As you can see, I got data only for 3 players, because player 4 has deleted his profile. Now my question is, how do I update basicDictionary to remove player 4?
I know that I'm supposed tu use if (!([basicDictionary isEqualToDictionary:refreshDictionary))
however, it won't tell me at which key they aren't equal.
So what would you guys do? Should I iterate through both of them in nested loop? That seems to consume alot of time when dictionaries are bigger. Oh by the way, my dictionaries are always sorted same way (by players id)
My idea is, that I compare these two dictionaries in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (refresh)
{ //compare dictionaries and modify basicDictionary that is data source for my tableview by deleting where key doesn't match
}
this code will give you keys that changed:
NOTE: as the lookup of keys is basically jumping into a hashmap (an o1 op), you dont have to worry about the 2nd array walk you talk about
#import <Foundation/Foundation.h>
#interface NSDictionary (changedKeys)
- (NSArray*)changedKeysIn:(NSDictionary*)d;
#end
#implementation NSDictionary (changedKeys)
- (NSArray*)changedKeysIn:(NSDictionary*)d {
NSMutableArray *changedKs = [NSMutableArray array];
for(id k in self) {
if(![[self objectForKey:k] isEqual:[d objectForKey:k]])
[changedKs addObject:k];
}
return changedKs;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDictionary *d1 = #{#"1":#"value",#"2":#"value",#"3":#"value",#"4":#"value"};
NSDictionary *d2 = #{#"1":#"value",#"2":#"newvalue",#"3":#"value"};
NSArray *ks = [d1 changedKeysIn:d2];
NSLog(#"%#", ks);
}
return 0;
}
Edit: self.allKeys changed to just self -- a dictionary can be enumerated already

Resources