I don’t how to achieve this. I’d like the user be able to set thresholds into a continuously degressive array. Results and thresholds appear in parallel columns (the table view has a controller).
Ex:
Key: “result” Key:“threshold”
20.0
19.5 ← first threshold here
19.0
18.5 ← second threshold here
18.0
…
In an ASOC app, using a tableview, an external (reverse) array, some radio controls and a data source, I managed to do it. Heavily, but with a good processor it’s fast enough.
In Objective-C and array controllers, it should be a better solution. In fact, it’s almost working. My only problem is: when the user moves a threshold, I should be able to erase the previous location (that was the goal of the (reverse) array, storing threshold locations – in the example above, the array had contained {19.5, 18.5.}.
Is it possible to retrieve a particular dictionary into an array, using only a particular value of one of its keys?
Of course you can do that, but I don’t think I understand what you’re trying to do. To find a particular object in an array you could use indexOfObjectPassingTest:, and in the block, query the value of the particular key you’re interested in. Here is a code snippet that shows, in general, how to use that method:
Well, imagine you want to attribute the «Fair» comment if the result is in range 12…16, «Good» if the result is in range 17…23 and «Excellent» if it’s above 23. These ranges depend of the maximal result (and the user appreciation). I want him to be able to fix the thresholds manually.
The best way is to put these thresholds on the same line than a certain result, so the tableview seems to fit this usage.
The user chooses a certain threshold (using a radio button) and clicks on the desired line. The tableview stores this threshold. So far, so good. The ObjC version works.
And now if the user changes his mind: letting the same radio button enabled, he clicks on another line. I have to retrieve where the threshold was, erase it, and put it on the new line.
My problem is. to retrieve this line. That’s why I used a sort of “pointers table” in ASOC, saying “this threshold is new” or “already stored in line xy”.
I guess this is what I don’t understand. What are you actually showing in the table? Don’t you want the same threshold to apply to all rows of the table? Are you showing “Fair”, “Good”, “Excellent” in one column, and what are you showing next to it in another column? Raw scores?
Yes, as in my first post. In ASOC version (with a unique -and large- IF test into the tableView_objectValueForTableColumn_row_ method) I could even note each scored result, so:
Column1 | Column2 | Column3
Thresholds | Points | Scores
| 20.0 |
| 19.5 | ¢ → This one is rated “Excellent”
| 19.0 |
Excellent | 18.5 | ¢¢ → These two are rated “Excellent”
| 18.0 | ¢ → This one is rated “Good”
| 17.5 | ¢¢¢¢ → These four are “Good” too
Good | 17.0 |
As soon as I shall be able to create objects (I work hard but only make Xcode show me yellow, rose and red warnings and errors) I’ll populate my apps with even useless objects! Abstracted, semi-abstarcted and visual objects! But here, a threshold inspires me more a encapsulated C-array like in a @implementation ArrayOfFloats.
Note that a threshold is a single bound value, not a range.
Anyway, thank you for your time. I’ll keep these examples in mind and sure be happy to retrieve them later!
There are always many different ways to tackle a particular problem. One option would be to use a value transformer, to return the “Fair”,“Good”, “Excellent” strings based on the point values and your thresholds. In the example below, both columns of the table are bound to arrangedObjects.num, but the column with the rating strings also has a value transformer in the bindings. I set up the transformer and the array in the app delegate. I used 3 text fields for entering the thresholds (but radio buttons would work as well), and have an action method for them that just forces the table to reload its data. The name that a is supplied in the setValueTransformer:forName: method is the name you use in the value transformer field in IB for the bindings:
@synthesize window = _window,theData,low,middle,high,tv;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//Instatiate the value transformer and register its name
PointsToRatingTransformer *transformer = [[PointsToRatingTransformer alloc]init];
[PointsToRatingTransformer setValueTransformer:transformer forName:@"ScoreToRating"];
//Set some initial values for the thresholds
self.low = [NSNumber numberWithFloat:0];
self.middle = [NSNumber numberWithFloat:12];
self.high = [NSNumber numberWithFloat:25];
// Create the array of point values from 30 to 1 in 0.5 point increments
self.theData = [NSMutableArray array];
float points = 30;
while (points > 1) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:points] forKey:@"num"];
[self.theData addObject:dict];
points -= .5;
}
self.theData = theData;
}
// Action method for the 3 text fields where threshhold values are changed
// The values of these text fields are bound to low, middle, and high
-(IBAction)updateTable:(id)sender {
[self.tv reloadData];
}
And the code for the subclass of NSValueTransformer is :
As you say, there are many way to solve this problem. I have to confess I felt back to procedural habits.
I finally used an other method than in my ASOC app. I fill up ALL values between thresholds, using an (ugly) C float array (all procedural languages use similar structures, so I had this reflex).
As thresholds are float values, the in-between values are float too. I made an (even more ugly) mix, filling a C array using Objective-C methods.
I end up with something like this:
Then when it comes to attributes a score, I only set the “result” field of the table to a particular value of the attrib array.
Well. Not a “state of the art” solution, I have to admit. Your solution is much closer to my ASOC solution, forcing the table to reload its data at each threshold modification. Mine just “reload” the C array before the attribution of the scores. And for the attribution, look at the ASOC way compared to the new one:
ASOC loop:
repeat with i from 1 to count of gResult
if item i of gResult is not missing value then
repeat with n from 2 to 12
if item n of gThresholds is not missing value then
if item i of gResult < (gMaximalPoints as real) - ((item n of gThresholds) * 0.5) then
set item i of gMark to normalize((n - 1) / 2)
exit repeat
end if
end if
set item i of gMark to gMaximalMark
end repeat
end if
end repeat
C/Objective-C loop:
Ok, maybe it’s not a language problem, but a programmer problem. But I understand what DJ said (in another post) about applications written in C/C++.