It's a fairly simple builtin in python for example: x = range(0,100)
How can I accomplish the same feat using objective-c methods? Surely there is something better than a NSMutableArray and a for-loop:
NSMutableArray *x = [NSMutableArray arrayWithCapacity:100];
for(int n=0; n<100; n++) {
[x addObject:[NSNumber numberWithInt:n]];
}
Yes, I am aware that doing this is most likely not what I actually want to do (ex: xrange in python), but humor my curiosity please. =)
Clarification: I would like a NSArray containing a sequence of NSNumbers, so that the array could be further processed for example by shuffling elements or sorting by an external metric.
If you want such an array, you might want to do your own specific subclass of NSArray.
A very basic implementation example would look like:
#interface MyRangeArray : NSArray
{
#private
NSRange myRange;
}
+ (id)arrayWithRange:(NSRange)aRange;
- (id)initWithRange:(NSRange)aRange;
#end
#implementation MyRangeArray
+ (id)arrayWithRange:(NSRange)aRange
{
return [[[self alloc] initWithRange:aRange] autorelease];
}
- (id)initWithRange:(NSRange)aRange
{
self = [super init];
if (self) {
// TODO: verify aRange limits here
myRange = aRange;
}
return self;
}
- (NSUInteger)count
{
return myRange.length;
}
- (id)objectAtIndex:(NSUInteger)index
{
// TODO: add range check here
return [NSNumber numberWithInteger:(range.location + index)];
}
#end
After that, you can override some other NSArray methods to make your class more efficient.
NSRange range = NSMakeRange(0, 100);
You can iterate this range by:
NSUInteger loc;
for(loc = range.location; loc < range.length; loc++)
{
}
Related
I'm all self taught so please keep the techincal jargin to a minimum. Theoretically if I had a method and it had a parameter that is the name of an array. How do I check to see if that array index 5 is equal to #"Yes" or #"No". I know it's one of these because its testing to see if the picture is appearing in the veiw controller. Here is an example:
-(void)methodName :(NSMutableArray *)arrayNameInMethod {
if ( [NSMutableArray *(arrayNameInMethod) indexOfObject:5] == #"Yes"){
//Hide a different picture assocciated with the Array
} else {
//Unhide a different picture assocciated with the Array
};
Also how do you do use the parameter "arrayNameInMethod" to replace the object. Basically:
if(Picture Clicked and picture is Unhidden) {
[NSMutableArray *(differentArrayNameInMethod) replaceObjectAtIndex:5 withObject: #"True)
};
(this is all in another method)
Comment #2: You can't use the parameters the same way because it's a string. You can't access an array with a name as a string.
Thank you so much!
I think what you will need is a dictionary, mapping the name to the array. I will give a barebones implementation:
#interface YourClass()
{
NSMutableDictionary *_arrayMap;
}
#end
#interface YourClass
- (instancetype)init
{
self = [super init];
if (self) {
// You might do this somewhere else, like viewDidLoad:
_arrayMap = [NSMutableDictionary new];
}
return self;
}
- (void)someMethod
{
// Some functionality to add a new array:
// Note the array contains NSNumber and NSString objects:
_arrayMap[#"sampleName"] = [#[ #(0), #"one", #(2.0), #"three", #(4), #(YES)] mutableCopy];
}
-(BOOL)checkForConditionInArrayNamed:(NSString *)arrayName
{
BOOL retval = NO;
NSMutableArray *array = _arrayMap[arrayName];
if ([array count] > 5) {
id obj = array[5];
if ([obj isKindOfClass:[NSNumber class]])
retval = [obj boolValue];
}
return retval
};
#end
You then call checkForConditionInArrayNamed: to check for the condition of the named array and act accordingly.
I want to use the variable of a for loop in a property name, example:
#interface
{
Player *rival1, *rival2, *rival3;
}
int number;
for (number=1; number<=3; number++){
rival+number = [[Player alloc] init]; //The compiler accepts this.
rival+number.name = #"";
//^This line gives the error: "use of undeclared identifier 'rival'"
//For the first loop, I want it work like: rival1.name = #"";
}
This isn't a particularly clean way of approaching this, but to answer the question as you've posed it:
- (void)generateRivals
{
for (int i = 1; i < 4; i++)
{
NSString *propertySetString = [NSString stringWithFormat:#"setRival%d", i];
Rival *rival = [[Rival alloc] init];
[self performSelector:#selector(NSSelectorFromString(propertySetString)) withObject:rival];
}
}
We create a selector (I haven't tested this, but it should work in theory) that corresponds to the getter for the property instance represented by i.
Based on comments you've added, it seems like what you really want is a variable number of players (perhaps not exceeding a certain number) with the ability to reference them individually.
Architecturally, rather than create properties pointing to each respective rival, put them all in an array (and keep it as a property on your class). So you'd create them like this:
- (void)generateRivals:(NSUInteger)numberOfRivals
{
NSMutableArray *rivalsArray = [[NSMutableArray alloc] initWithCapacity:numberOfRivals];
for (int i = 0; i < numberOfRivals; i++)
{
Rival *rival = [[Rival alloc] init];
[rivalsArray insertObject:rival atIndex:i];
}
[self setRivalsArray:rivals];
}
Then, when you need to access a particular rival, call a method like this, which will return the rival at the index number you pass:
- (Rival *)rivalWithNumber:(NSUInteger)number
{
return [[self rivalsArray] objectAtIndex:number];
}
you cant create the properties variable names on the fly and use it, as the names get lost during compilation
you can put them in an array and enumerate over it
Player rival1 = [[Player alloc] init];
Player rival2 = [[Player alloc] init];
Player rival3 = [[Player alloc] init];
for (Player *p in #[rival1, rival2, rival3]) {
p.name = #"";
}
probably the best way would be to maintain the rivales in the array from the beginning
#interface …
#property(strong) NSArray *rivals;
#end
#implementation …
//in some useful method, like initializer
_rivals = [NSMutableArray array]
for (int i = 0; i < 3 ++i){
//create player and add to array
}
#end
You might want to look into Key Value Coding.
You need to create properties for your rivals
#interface …
#property(strong) Player *rival1;
#property(strong) Player *rival2;
#property(strong) Player *rival3;
#end
#implementation …
-(id)init…
{
if(self = [super init…]){
for (int i = 1; i < 4; ++i) {
Player *rival = [[Player alloc] init];
rival.name = [NSString stringWithFormat:#"rival%d", i];
[self setValue: rival forKey:[NSString stringWithFormat:#"rival%d", i]];
}
}
return self;
}
#end
Instead of running a for loop to set the names of the players to #"" just add the line
name = #"" in your init method of your Player class. It's faster and more practical than to write a for loop. You can also create a method like
-(id)initWithName:(NSString *)initName{
self = [super init];
if (self){
name = initNane;
}
return self;
}
to set the name of your player at the moment it's created. Although if you want absolutely a for loop, you can use the method posted by vikingosegundo previously.
I'm trying to make a function for a NSMutableArray subclass that only uses integer, but I don't want to use "count." How do I do this?
-(NSMutableArrayWithIntegers*)initWithCount:(NSInteger)count numbers:(NSInteger)firstInt, ...
{
self = [super init];
if (self) {
va_list args;
va_start(args, firstInt);
NSInteger arg = firstInt;
for (int i = 0; i < count; i++)
{
arg = va_arg(args, NSInteger);
[self addObject: [NSNumber numberWithInteger:arg]];
}
va_end(args);
}
return self;
}
I know this doesn't answer your question but it's important to let you know. Don't ever subclass NSMutableAnything. Use a category and thank me later:
#interface NSMutableArray (ListOfIntegers)
+(NSMutableArray)mutableArrayWithIntegers:(NSInteger)i, ... {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:whatever];
// do your thing
return array;
}
#end
First of all, the approach you currently have is just fine. Don't try getting rid of the count. There are alternatives, but they are only worse.
For example, you may use a sentinel value (which may not be inserted into the array) as the last argument, but in this case, you will have to make sure that you are not actually trying to insert this value to the array at all:
- (id)initWithIntegers:(NSInteger)first, ...
{
if (!(self = [super init])) return nil;
va_list args;
va_start(args, first);
NSInteger n;
if (first != NSIntegerMax) {
[self addObject:#(first)];
while ((n = va_arg(args, NSInteger)) != NSIntegerMax) {
[self addObject:#(n)];
}
}
va_end(args);
return self;
}
But really, this unnecessarily narrows the range of values that can be added - using that count argument is not a big deal.
I'm creating UILabels dynamically in a for each loop. Every loop that is run creates 1-4 UILabels.
What I want is that I put these UILabels into my NSMutableArray and being able later to easy retrieve the data.
My original thought was to put these UILabels into a NSDictionary and use [dictGroupLabels setValue:uiLabel1 forKey:#"uiLabel1"] and then [dictGroupLabels setValue:uiLabel2 forKey:#"uiLabel2"] and so on. And then put this dictionary into my NSMutableArray for each loop. Later on I could access the values like UILabel *label = [[myArray objectAtIndex:0] valueForKey:#"uiLabel1"] BUT that unfortunately doesn't work since UILabels don't conform to the NSCopying protocol.
So with this in mind how would you solve this?
this question provided more information on what you are trying to accomplish. Since you know for a fact, the possible set of labels you are trying to create in each case, I would highly recommend using mutable dictionaries instead of arrays.
To illustrate, given the following hypothetical class definition:
#interface MyClass: NSObject {
NSMutableDictionary * _labelDict;
}
#property (nonatomic, retain) NSMutableDictionary * labelDict;
- ( void )methodA;
- ( void )methodB;
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx;
#end
You would have the following, hypothetical, class implementation:
#implementation MyClass
#synthesize labelDict = _labelDict;
- ( id ) init {
if( ( self = [ super init ] ) ) {
[self setLabelDict: [NSMutableDictionary dictionaryWithCapacity: 8]];
}
}
- ( void ) dealloc {
[ self.labelDict release ];
[ super dealloc ];
}
- ( void ) methodA {
for(NSUInteger i = 0; i < some index; i++) {
[self.labelDict setObject: [self labelsForRunLoop: i] forKey: [NSString stringWithFormat: #"%d", i]];
}
}
- ( void ) methodB {
// Locate the label you need to work with. Example based on this crude pseudo code
NSMutableDictionary * subDict = (NSMutableDictionary *) [self.labelDict objectForKey: #"0"];
UILabel * theLabel = (UILabel * ) [subDict objectForKey: #"UILabel.Z"];
theLabel.text = #"Label 1";
}
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithCapacity: 4] ;
[dictionary setObject: create-w-label forKey: #"UILabel.W"];
[dictionary setObject: create-x-label forKey: #"UILabel.X"];
[dictionary setObject: create-y-label forKey: #"UILabel.Y"];
[dictionary setObject: create-z-label forKey: #"UILabel.Z"];
return [dictionary retain];
}
#end
This is basically pseudo code and will not successfully compile. However it will serve as a good starting point. You probably want to store each label dictionary under some key that makes sense, instead of just using the loop's index. Hope this helps.
They don’t need to adhere to NSCopying to be added to an array. It sounds like you just need to do something like this:
NSMutableArray *mainArray = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
NSMutableArray *subArray = [[NSMutableArray alloc] initWithCapacity:5];
for(int j = 0; j < 4; j++)
{
UILabel *label = [[UILabel alloc] init];
// etc.
[subArray addObject:label];
[label release];
}
[mainArray addObject:subArray];
[subArray release];
}
// then, to get one of the labels:
UILabel *someSpecificLabel = [[mainArray objectAtIndex:2] objectAtIndex:1];
I have a large NSArray of names, I need to get random 4 records (names) from that array, how can I do that?
#include <stdlib.h>
NSArray* names = ...;
NSMutableArray* pickedNames = [NSMutableArray new];
int remaining = 4;
if (names.count >= remaining) {
while (remaining > 0) {
id name = names[arc4random_uniform(names.count)];
if (![pickedNames containsObject:name]) {
[pickedNames addObject:name];
remaining--;
}
}
}
I made a caregory called NSArray+RandomSelection. Just import this category into a project, and then just use
NSArray *things = ...
...
NSArray *randomThings = [things randomSelectionWithCount:4];
Here's the implementation:
NSArray+RandomSelection.h
#interface NSArray (RandomSelection)
- (NSArray *)randomSelectionWithCount:(NSUInteger)count;
#end
NSArray+RandomSelection.m
#implementation NSArray (RandomSelection)
- (NSArray *)randomSelectionWithCount:(NSUInteger)count {
if ([self count] < count) {
return nil;
} else if ([self count] == count) {
return self;
}
NSMutableSet* selection = [[NSMutableSet alloc] init];
while ([selection count] < count) {
id randomObject = [self objectAtIndex: arc4random() % [self count]];
[selection addObject:randomObject];
}
return [selection allObjects];
}
#end
If you prefer a Swift Framework that also has some more handy features feel free to checkout HandySwift. You can add it to your project via Carthage then use it like this:
import HandySwift
let names = ["Harry", "Hermione", "Ron", "Albus", "Severus"]
names.sample() // => "Hermione"
There is also an option to get multiple random elements at once:
names.sample(size: 3) // => ["Ron", "Albus", "Harry"]
I hope this helps!