Removing Random Items From NSMutableArray - ios

I have a program where a user enter strings into an NSMutableArray (myArray) via a Text Field. This array is passed into the next view controller where there is a label (myLabel) and two buttons. Printed to the label is a random string from myArray. ButtonA displays a different random string from the array when pressed and ButtonB removes the current string that is printed to the label and then displays a random string from the array to the label.
This is my current solution:
- (void)viewDidLoad {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
-(IBAction)ButtonA:(id)sender {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
-(IBAction)ButtonB:(id)sender {
NSInteger index = [myArray indexOfObject: //what goes here?];
[self.myArray removeObjectAtIndex:index];
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
Is there a way to get the index of the random string displayed and then remove it from the array? I want this to continue doing this until all items from the array have been removed. Thank you

The // what goes here? should simply be self.myLabel.text.
Though it might be better to add an instance variable that saves off the last random index. Then all array index references should be made with that instance variable.
You also have the same line of code to calculate a random number and set a label repeated 3 times. Create a new method for that and call that function from the three places you have it now.

There are two ways you can do this:
The first way is to store the string you got from your random method. You can declare a global variable for this in your class. And I suggest that you always place a block of similar code in another method.
NSString *generatedString;
- (NSString *)generateRandomString
{
generatedString = [self.myArray objectAtIndex:arc4random() % [myArray count]];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedString];
self.myLabel.text = [self generateRandomString];
}
Another way is to store the index of the string generated:
NSInteger generatedStringIndex;
- (NSString *)generateRandomString
{
generatedStringIndex = arc4random() % [myArray count];
NSString generatedString = [self.myArray objectAtIndex:generatedStringIndex];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedStringIndex];
self.myLabel.text = [self generateRandomString];
}

Related

Calling UITableView datasource methods beforehand when the view is not present

I have dropped my code into a situation where I need to call UITableView data source methods written in some UIViewController class before a particular view is presented so that the cells get prepopulated and I can set a BOOL that the data in the not present viewController class is valid or not. I may explain it in more detail if required, but I wanted to know if its possible to do that. If yes, then how to do it? .. as a particular set of my code written after [tableView reloadData] is dependent on running the dataSource methods of UITableView. Please throw some light on this, if needs to be handled in a specific thread?
Following is the case where I call reloadData. Note: This is happening in another class when basicFactsViewController's viewWillAppear method has not been called yet:
- (BOOL) isComplete {
dispatch_async(dispatch_get_main_queue(), ^{
[basicFactsViewController.tableView reloadData];
});
return basicFactsViewController.isComplete && selectedVehicleId && selectedMakeId && selectedModelId && selectedYearId && selectedTrimId;
}
Now basicFactsViewController.isComplete is checked in this method:
- (BOOL) isComplete {
[self collectKeyHighlights];
return _isComplete;
}
Now the dictionary "tableCells" in the method below uses the cells population to check whether all features have been completed or not:
- (NSDictionary *) collectKeyHighlights {
NSMutableDictionary *key_highlights_update = [NSMutableDictionary new];
NSMutableDictionary *cell_highlight_update = [NSMutableDictionary new];
if(visible_key_highlights.count == 0) _isComplete = YES;
_isComplete = YES;
__block NSMutableArray *reloadCellAtIndexPathSet = [[NSMutableArray alloc] init];
[visible_key_highlights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *feature = (NSDictionary *)obj;
UITableViewCell *cell = [self.tableCells objectForKey:[NSIndexPath indexPathForRow:idx inSection:0]];
if(cell) {
if([cell isKindOfClass:[DRColorSelectionTableViewCell class]]) {
NSInteger selectedIndex = ((DRColorSelectionTableViewCell *)cell).selectedIndex;
NSInteger numberOfSegments = ((DRColorSelectionTableViewCell *)cell).numberOfSegments;
if(selectedIndex > -1 ) {
NSArray *dataValues = [[visible_key_highlights objectAtIndex:idx] objectForKey:#"data_values"];
NSDictionary *colorData;
BOOL reloadCellForIndexPath = NO;
if (numberOfSegments == selectedIndex) {
colorData = #{ #"normalized" : #"user_defined", #"isother" : #YES, #"hexcode":#"#FFFFFF", #"actual":((DRColorSelectionTableViewCell *)cell).otherColorTextField.text};
reloadCellForIndexPath = YES;
}
else{
colorData = [dataValues objectAtIndex:selectedIndex];
}
[key_highlights_update setObject:colorData forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:colorData forKey:[feature objectForKey:#"name"]];
if (![colorData isEqual:[prevSelections objectForKey:[feature objectForKey:#"name"]]]) {
[reloadCellAtIndexPathSet addObject:((DRColorSelectionTableViewCell *)cell).indexPath];
}
//if (reloadCellForIndexPath) {
//}
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRInputTableViewCell class]]) {
NSString *textInput = ((DRInputTableViewCell *)cell).inputTextField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
}else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRPickerTableViewCell class]]) {
NSString *textInput = ((DRPickerTableViewCell *)cell).inputField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRSwitchTableViewCell class]]) {
// send this everytime for now
BOOL isSelected = ((DRSwitchTableViewCell *)cell).toggleButton.selected;
[key_highlights_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
}
}
else{
_isComplete = NO;
}
}];
prevSelections = cell_highlight_update;
if ([reloadCellAtIndexPathSet count]) {
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:reloadCellAtIndexPathSet withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
return key_highlights_update;
}
Now here since
[tableView reloadData]
is not calling cellForRowAtIndePath:, hence, tableCells is not getting populated, hence, I am always getting _isComplete = NO.
If I understand correctly, there is processing being done when the tableview loads (calls it's dataSource methods) and you want to trigger that early to use its results. Calling [basicFactsViewController.tableView reloadData]; early won't work if the basicFactsViewController hasn't been displayed yet. If basicFactsViewController is a UIViewController and has the default view and the tableView property is a subview of that standard view, then (if I remember correctly) the tableView property will be nil until the basicFactsViewController has been displayed. A shortcut around that is to access the viewController's view property and cause it to initialize (viewDidLoad and all that). You can do that by simply messaging the viewController: [basicFactsViewController view].
If I've been right so far I'm fairly confident that will initialize the tableView property. But I'm not sure if it will cause the table view to load its data. And even if it does work, it's definitely not the best solution to the piece of code you're trying to architect. Apple's design for UIKit has been focused on the model/view/controller pattern and it's easier to go with the flow and do the same. I imagine that you could move the processing that is in the data source methods for the tableView out into another class (or maybe even the same class), and call that method to get everything ready for both the tableView and any other checks that you have, storing the data in dictionaries and arrays in such a way that you can easily load them by index into the tableView when cellForIndex is called.

Removing Duplicate Custom Objects from two arrays and combining them

First off i have searched a lot but all methods seems to be for primitives or for whole custom objects.
My situation is this. I have a type custom objects in two different arrays. However the fields of every single objects is quite different to another with the exception of only 2 fields.
What i want is combine both of these arrays and then remove duplicates with respect to only those two fields.How can i do that. My Code so far
NSMutableArray* testArray = [eventHandler returnAllEvents];
NSMutableArray* combinedArray = [[NSMutableArray alloc]init];
NSArray* finalArray = [[NSArray alloc]init];
if (testArray.count==0) {
for (int i = 0; i<facebookData.count; i++) {
LSEvent* event = [facebookData objectAtIndex:i];
[combinedArray addObject:event];
}
finalArray = [combinedArray arrayByAddingObjectsFromArray:calendarData];
}
NSMutableArray *uniqueArray = [NSMutableArray array];
NSMutableSet *names = [NSMutableSet set];
for (id obj in finalArray) {
NSString *destinationName = [obj destinationname];
if (![names containsObject:destinationName]) {
[uniqueArray addObject:obj];
[names addObject:destinationName];
}
}
You can do sth like this
NSArray first = ...
NSMutableArray second = ... // this will be combine array
for (id someObj in first) {
if ( [second filteredArrayUsingPredicate:[self predicateForObject:someObj ]].count == 0 ){
[second addObject: someObj];
}
}
If you want to check that object exists in array using containsObject: you need to implement - (BOOL)isEqual:(id)other in your custom object.
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
}
if (!other || ![other isKindOfClass:[self class]]) {
return NO;
}
if (self.identifier == other.identifier) {
return NO;
}
return YES;
}

Reading Value from UIDatePicker UIDatePickerModeCountDownTimer

Trying to get the amount of time selected by a user from a UIDatePicker set to mode:"UIDatePickerModeCountDownTimer" in InterFace Builder. Some other stacks say to just check the value of the property countDownDuration on the object.
#property (weak, nonatomic) IBOutlet UIDatePicker *hereIsThePicker;
...
NSLog(#"%f", self.hereIsThePicker.countDownDuration);
But this value often reports a range of values, I've seen numbers such as 70, 80, 113, etc for a selection of "0 Hours, 0 Minutes". So apparently countDownDuration is not what I need.
Does anyone have any hints or clues on how to use UIDatePickerModeCountDownTimer and convert the selection to number of seconds selected? so 1 Minute Selected = 60 Seconds, or 1 Hour 5 Minutes Selected = 3900 seconds ?
Been trying to track this down for 2 days now and just can't seem to get my head around what is going on.
Here is a screenshot of an example application that I setup that just has these two components. You can see the IBOutlet, and a button with an IBAction that is fetching the time and NSLogging it to the bottom of the screen:
Finally after trying random stuff for days, the simple answer, for whatever reason is you need to set a value to countDownDuration, even if you don't need to:
[self.timerPicker setCountDownDuration:60.0f]; //Defaults to 1 minute
In your header .h file put this
#interface ViewController : UIViewController
{
__weak IBOutlet UIPickerView *datePick;
NSMutableArray *hoursArray;
NSMutableArray *minsArray;
NSTimeInterval interval;
}
- (IBAction)calculateTimeFromPicker:(id)sender;
in your .m file put this
- (void)viewDidLoad
{
[super viewDidLoad];
//initialize arrays
hoursArray = [[NSMutableArray alloc] init];
minsArray = [[NSMutableArray alloc] init];
NSString *strVal = [[NSString alloc] init];
for(int i=0; i<61; i++)
{
strVal = [NSString stringWithFormat:#"%d", i];
//NSLog(#"strVal: %#", strVal);
//Create array with 0-12 hours
if (i < 13)
{
[hoursArray addObject:strVal];
}
//create arrays with 0-60 mins
[minsArray addObject:strVal];
}
NSLog(#"[hoursArray count]: %d", [hoursArray count]);
NSLog(#"[minsArray count]: %d", [minsArray count]);
}
//Method to define how many columns/dials to show
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
// Method to define the numberOfRows in a component using the array.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent :(NSInteger)component
{
if (component==0)
{
return [hoursArray count];
}
else
{
return [minsArray count];
}
}
// Method to show the title of row for a component.
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
switch (component)
{
case 0:
return [hoursArray objectAtIndex:row];
break;
case 1:
return [minsArray objectAtIndex:row];
break;
}
return nil;
}
- (IBAction)calculateTimeFromPicker:(id)sender
{
NSString *hoursStr = [NSString stringWithFormat:#"%#",[hoursArray objectAtIndex:[pickerView selectedRowInComponent:0]]];
NSString *minsStr = [NSString stringWithFormat:#"%#",[minsArray objectAtIndex:[pickerView selectedRowInComponent:1]]];
int hoursInt = [hoursStr intValue];
int minsInt = [minsStr intValue];
interval = 0 + (minsInt*60) + (hoursInt*3600);
NSString *totalTimeStr = [NSString stringWithFormat:#"%f",interval];
}

Change index of specific char of NSString

I have a string, for example "Soccer". Now I want to move every "e" by lets say 2 indexes(right word?), so my string looks like this = "erSocc". This has to work with whitespace and negative/- indexes.
I came a cross with this, not perfect working, solution:
NSString* text = #"Soccer";
NSString* sign = #"c";
int index = 1;
NSMutableArray* arrayText = [[NSMutableArray alloc]init];
NSMutableArray* arraySignNewPosition = [[NSMutableArray alloc]init];
NSMutableArray* arrayOldSignPosition = [[NSMutableArray alloc]init];
for(int i=0;i<(text.length);i++)
{
[arrayText addObject:[text substringWithRange:NSMakeRange(i, 1)]];
if ([[arrayText objectAtIndex:i]isEqualToString:sign])
{
[arrayOldSignPosition addObject:[NSNumber numberWithInt:i]];
if ((i+index)>(text.length-1))
{
int indexDifference = (i+index)-(text.length);
[arraySignNewPosition addObject:[NSNumber numberWithInt:indexDifference]];
}
else
{
[arraySignNewPosition addObject:[NSNumber numberWithInt:(i+index)]];
}
}
}
for (NSNumber* number in arraySignNewPosition)
{
[arrayText insertObject:sign atIndex:number.integerValue];
if(number.integerValue-index>0)
{
[arrayText removeObjectAtIndex:(number.integerValue-index)];
}
else
{
[arrayText removeObjectAtIndex:((arrayText.count-1)+(number.integerValue-index))];
}
}
I know the code is not working perfectly, but I would like to know if this is the right way or if there are some Cocoa functions I could use to accomplish my goal. Thanks for your time.
You're really just getting substrings and moving them around, so you could do something like this:
- (NSString *)shiftRight:(NSUInteger)places
{
NSAssert(places > 0, #"places must be greater than 0");
NSAssert(places < [self length], #"places must be less than the length of the string");
places = [self length] - places;
NSString *start = [self substringFromIndex:places];
NSString *end = [self substringToIndex:places];
return [start stringByAppendingString:end];
}
Here's a complete code listing, with examples.

Remove object from NSMutable array wont work

I have an NSMutable array which i'm trying to remove objects from and by that to decrease
the count of number of objects on the array, and the count always stays the same,
Meaning, the remove is not working.
Here is the code (it's from the online stanford IOS development course):
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (void)addCard:(Card *)card
{
[self addCard:card atTop:NO];
}
- (Card *)drawRandomCard
{
Card* randomCard = Nil;
NSLog(#"This is the count %d",[self.cards count]);
if ([self.cards count]) {
unsigned index = arc4random() % [self.cards count];
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
The count is always 52, even after removing the objects.
Any ideas on how to fix this?

Resources