Here's what I'm looking to do:
I have a series of survey questions that are generated randomly for the user.
What I want is to associate two values with each other, the integer value of the question number that was generated randomly, and a integer value representative of how many times this user has answered this question. I want to be able to save and retrieve these associated values later so I can increment how many times the user has answered this question.
So, how do you associate two different integer values to each other, save, and update them later?
I'm starting with this code.
QuestionSelected = arc4random_uniform(4);
For example once this outcome was generated:
QuestionSelected = 1
I would need to establish a new value that I can associate with that question's value (1), increment it, and then save each time the user returns to this question.
Thanks for your help!
Association of one value with another is a "map". NSDictionary is the standard mapping collection in Cocoa, so you can use that:
NSNumber * timesSeen = myMutableDictionary[#(selectedQuestion)];
if( !timesSeen ){
timesSeen = #(0);
}
myMutableDictionary[#(selectedQuestion)] = #([timesSeen intValue] + 1);
Since an array in essence associates its indexes, which are numbers, with values, another option is to use an NSMutableArray:
NSNumber * timesSeen = myMutableArray[selectedQuestion];
myMutableArray[selectedQuestion] = #([timesSeen intValue] + 1);
An NSArray can't be sparse, so you would have to fill it up with zeroes when you create it:
for( NSUInteger i = 0; i < numQuestions; i++ ){
[myMutableArray addObject:#(0)];
}
As you may notice, dealing with numerical values that need to change in a Cocoa collection is a bit of a pain, because the NSNumbers have to be unboxed and replaced each time. So a third option would be a C array:
int * viewCounts;
viewCounts = calloc(numQuestions, sizeof(int));
viewCounts[selectedQuestion]++;
calloc() gets a chunk of memory for you and fills it with zeros. You will need to release that memory by calling free(viewCounts) when you are done with the array.
Related
I was looking around and couldn't find anything, and I'm starting to think it's not possible with objective-c.
I have a NSMutableArray *myMutableArray and the size varies depending on what csv file is loaded. Since I do not set a size of myMutableArray I can't do:
if (c == 5){
myMutableArray[q] = [[NSNumber numberWithFloat:myOtherArray] stringValue];
q = q + 1;
c = 0;
}
Else {
c = c + 1;
}
Since myMutableArray is technically of size nil I guess I can't add objects to it.
In cases, q can be between 1500 and 2500.
My question is, how do I make `myMutableArray' change size on every loop.
If this isn't possible, I guess I will have to make myMutableArray very large - but I need the values in myMutableArray for a graph. If I do myMutableArray = [[NSMutableArray alloc] initWithCapacity:5000]; and don't use the 5000 memory locations, will these locations be nil, or 0? (sorry if the technical words are wrong, by memory locations I mean the memory given to myMutableArray)
Thank you, if there is anything else I can add to this please feel free to let me know.
EDIT: What I'm trying to achieve is adding data to the array, and with that data create a graph
You can't have a sporadically populated array. Creating an array with capacity 5000 is just a hint as to how much data you might be going to store into the array. Consider using an array of dictionaries where the dictionary contains the q value (presumably one coordinate) and the associated value. You can then sort the array based on the q values if you need to (for plotting). Then you can just add the dictionaries to the array as usual (addObject:).
The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray.
If you
arrayWithCapacity:
Creates and returns an NSMutableArray object with enough allocated memory to initially hold a given number of objects.
Mutable arrays expand as needed. When declaring them, we can init them like this:
+ (instancetype)arrayWithCapacity:(NSUInteger)numItems
Here numItems simply establishes the object’s initial capacity.
Later to add more data, i.e. to expand mutable array, use this
addObject:
What it does is, it inserts a given object at the end of the mutable array.
- (void)addObject:(id)anObject
It's important to note that:
The object to add to the end of the array's content. This value must not be nil. It raises an NSInvalidArgumentException if anObject is nil.
I am randomly generating 6 numbers using
self.number1 = [[NSNumber alloc] initWithInt:((arc4random() %
(hightestNumber-lowestNumber+1)) + lowestNumber)];
I do that for all six numbers, but some of the numbers come out the same. What code can I use to make sure that not any two or more numbers are the same value. I was going to use an if statement to compare the number to all six numbers and then call the arc4random code again but there is a possibility of it coming out the same value of another number again and I would have to test it again after. I really am stumped on this one. Thank you for your help.
What code can I use to make sure that not any two or more numbers are the same value.
A set doesn't have duplicate values, so one option is to add numbers to a set until the size of the set reaches the number of objects you want (6, in your case).
NSMutableSet *set = [NSMutableSet set];
while ([set count] < 6) {
[set addObject:#((arc4random() % (hightestNumber-lowestNumber+1)) + lowestNumber)];
}
Another option is to pick numbers from an existing list, removing each one you choose:
NSArray *list = #[#5, #6, #7, #8, #9, #10, #11, #12, #13];
NSMutableArray *sourceList = [list mutableCopy];
NSMutableArray *finalList = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
// (omitting code to choose a random index for brevity)
[finalList addObject:[sourceList objectAtIndex:randomIndex]];
[sourceList removeObjectAtIndex:randomIndex];
}
This method can work well if the list you're choosing from contains consecutive (or at least monotonically increasing) numbers, since that makes it easy to guarantee that the values in the source list are unique.
In addition to Caleb's excellent suggestions, if the range is relatively small you can create an array with all the values enumerated, shuffle them, and then pick the first 6.
I have a collection of int64_t values that I need to make an index set out of. I previously have used:
NSIndexSet *set = [NSIndexSet indexSetWithRange:NSMakeRange(location, length)];
to make index sets, but this time its unclear to me how I would do this based on the information I have.
Specifics: I have a collection of values. I know the highest value in the collection, the lowest value in the collection and the number of values in the collection. However, the values are not necessarily consecutive.
For example, if I have the following values: 1,3,4,6,7,9 I would like to create a set that includes all numbers in the collection between and including 1 and 9, i.e. set = 1-9.
How would I do this?
Edit
I shouldn't say I have a "collection" of integers, rather - I have objects stored in core data and each object has an int64_t attribute associated with it, so I don't have a pointer to an actual collection of the objects, and I want to avoid fetching the objects just to create a collection.
Edit: As it turned out in the discussion, the question was how to create a range
with a given minimal and maximal value. This is done with
NSMakeRange(min, max + 1 - min)
Just use a NSMutableIndexSet and add them one by one with the method addIndex
You could do something like this:
NSArray *array = #[#1,#3,#4,#6,#7,#9];
NSMutableIndexSet *set = [[NSMutableIndexSet alloc] init];
for (NSNumber *value in array) {
[set addIndex:[value integerValue]];
}
In my application, I'm receiving a CSV file that contains 30,000 objects and for each object there are always 24 values (a total of 720,000 values).
Format is something like this:
object1,value1,value2,...,value24
object2,value1,value2,...,value24
...
objectn,value1,value2,...,value24
When I parse this file, I convert each row in an NSArray of NSString.
Next I do the following for each value of the array:
convert from NSString to float using - (float)floatValue
convert the float to an NSNumber
store the NSNumber in an NSMutableArray
This process takes several seconds and from Instruments Time Profiler I'm spending 3.5 s in step 2 & 3 for the 720,000 values.
How can I proceed to avoid the NSNumber translation? Can I use a C style array, something like []? Or CFMutableArrayRef? If it helps, I know there are always 24 values for each object.
Thanks for the help,
Sébastien.
Depending on how you plan to use these values later, there are different ways.
Store entire float array as single NSValue. Pros: construction 24x faster. Cons: you must extract all items to access any of them.
Keep values as strings. Pros: no time wasted. Cons: frequent accesses will waste time.
Design a class that keeps single record: one NSString and 24 float properties. Pros: single record rules everything. Cons: single record rules everything.
upd: If you think of inconvenience manually naming 24 fields value1 .. value24 in case 3, feel free to declare public array in interface section of your class. This will combine nativity of record object with c-style array. You may also add -[valueAtIndex:] and -[setValue:atIndex:] methods to that class and make real array private.
Personally I'd just use a C-style array. If you want to process the data row by row, you could have an object representing each row, something like this:
#interface Row : NSObject {
float values[24];
}
#end
Then you create a Row instance for each row, set the 24 values directly, and add the instance to a NSMutableArray.
Row *row = [[[Row alloc] init] autorelease];
// here's where you read in the data for the row and save the 24 values
row.values[0] = ...
...
row.values[23] = ...
// and here you add the Row instance to an NSMutableArray
[rows addObject:row];
Otherwise, if you know up front you're going to be expecting 30,000 rows then you could preallocate a 30,000 x 24 array of floats.
float *rows = calloc(30000*24, sizeof(float));
for (int i = 0; i < 30000; i++) {
float *values = rows[24*i];
// here's where you read in the data for row i and save the 24 values
values[0] = ...
...
values[23] = ...
}
Just don't forget you'll need to free the memory from that calloc when you're done with it.
Is there any way to assign a set of values to an enum and check wether a variable has its value in that enum in objective-c.
Something like getting the index of an object in an array if the object is present in an array. I want to give a common behavior, when certain indexes(may not be in order) tapped in my table view, want to avoid if-ing all the indexes, hope I can use enum style.
Objective-C, like most programming languages, doesn't provide a "type membership" test for primitive types - just for object types (e.g. isKindOfClass:) - so using an enum won't solve your problem.
However given that you are thinking of an enum in the first place it sounds like you have a small fixed set of values you wish to check for. In that case you can just use a static array, e.g. something like:
static NSUInteger indices[] = { 2, 42, 57 };
The number of values in such an array is given by sizeof(indices) / sizeof(NSUInteger and you can write a simple loop or binary search to test for membership.
If you wish to use objects then NSSet is a good choice, if the numbers will vary during execution then NSMutableSet. E.g.
NSSet *indices = [NSSet setWithObjects:#2, #42, #57, nil];
and you test for membership using member::
if ([indices member:#indexToTest] != nil) ...
(Note: unlike NSArray the specification for containsObject: for NSSet does not say it uses isEqual:, hence the use of member: here.)
You can't use an enum for that, because you can't enumerate them or call sth. like contains on them. If I understand your correctly I think the straight forward way to do that would be to just use an NSArray to store the indices you care about. Say you want to recognize the indices 2,6 and 14:
NSArray *indices = [NSArray arrayWithObjects: #2, #6, #14, nil];
(The # is necessary because an NSArray can only store objects. The # turns the integers into NSNumber instances.
Then to check if the currently selected index is in it you use:
int indexToCheck = ...;
BOOL indexIsInArray = [index containsObject:#(indexToCheck)];
If your looking for a more 'fancy' (and faster) way to that, you could use bit operations (again using 2,6 and 14 as example):
int indices = 1 << 2 | 1 << 6 | 1 << 14;
and then to check:
int indexToCheck = ...;
BOOL isValid = (1 << indexToCheck) & indices;
where isValid is true when the indexToCheck in one of your indices.
No, internally enums are based on integers, and the mapping happens at compile time. So you need a specific check for every enum type (if they are consecutive numbers, a ">" and "<" check will do).