I have 2 tables like this
As you can see, if you look at Total you can see the score of each player in 3 rounds. I have to do a list (from the 1st to the 12th) indicating the highest score.
Here the player with 28 points, must have the number 1 (instead of that 8 which is generated by default), the player with 22 must have the number 2 instead of 11... So I have to sort the TOTAL columns and return the position in the correct label.
When I click the button I underlined, the procedure is called:
var vettore:array[1..12] of integer;
indici:array[1..12] of integer;
i:smallint;
begin
for i := 1 to 6 do
begin
vettore[i]:= StrToInt(StringGrid1.Cells[5,i]); //col,row
indici[i] := i;
end;
for i := 6 to 12 do
begin
vettore[i]:= StrToInt(StringGrid2.Cells[5,i]); //col,row
indici[i] := i;
end;
In this way I load inside vettore
all the TOTAL numbers in the rows of both tables, and in indici
you can find the number of the label on the right of the table (they indicates the position). Now I thought I could use any sorting method since I have only 12 elements (like the Quick Sort).
My problem is this: how can I change the labels texts (the ones on right of the tables) according with the sorted array? It's like the picture above shows.
Every label is called (starting from 1) mvp1
, mvp2
, mvp3
, mvp4
... I think this can be helpful because if (maybe) I will have to do a for loop for change the text of each label, I can use a TFindComponent
.
If it could be helpful, here there is the function I wrote with javascript on my website (it works):
var totals = [], //array with the scores
indices = []; //array with the indices
for (var i=0; i<6; i++) {
totals[i] = parseInt(document.getElementById('p'+i).value, 10);
indices[i] = i;
}
for (var i=6; i<12; i++) {
totals[i] = parseInt(document.getElementById('p'+i).value, 10);
indices[i] = i;
}
indices.sort(function(a, b) {
return totals[b]- totals[a];
});
for (var i=0; i<indices.length; i++) {
document.getElementById('mvp'+(indices[i]+1)).value = (i+1);
}
AS. Since only delphi is listed in tags, that means that any Delphi version is okay. I'd refer to delphi-xe2.
1st we would use Advanced Records to hold the data for a single participant. Some links are below, google for more.
.
The expression
Self.Total * ( 1.0 / Length(GP) )
; can be also written asSelf.Total / Length(GP)
. However i'd like to highlight some Delphi quirks here.3 div 2 = 1
and3 / 2 = 1.5
. Choosing wrong one causes compilation errors at best and data precision losses at worst.Length
to float, but Delphi does not support it. So i multiply by 1.0 to cast. Or i may add 0.0.1 / value
into a temp variable, and then mutiply each element by it instead. Since GP is of fixed size, it is compiler that calculates(1.0 / Length(GP))
and substitutes this constant. If you would allow different clans to have different amount of games - and turn GP into being dynamic arrays of different sizes - you would be to explicitly add a variable inside the function and to calccoeff := 1.0 / Length(GP);
before loop started.Now we should make a container to hold results and sort them. There can be several approaches, but we'd use generics-based
TList<T>
.The TList is an object, so you would have to CREATE it and to FREE it. I think you can make it a PUBLIC property of your MainForm, then create the list in
TMainForm.OnCreate
event and free it inTMainForm.OnDestroy
event.Another, lazier approach, would be using a regular
dynamic array
and its extensions.However, i'll use TList below. Again, i assume that other routines in you program already and correctly create and destroy the given
var ClanData: TList<TClanResults>;
object instance.Now finally we need to know how to show that table on the screen. I would use typical
TStringGrid
as the most simplistic widget. I suggest you to look some advanced string grid from JediVCL or something from Torry.net so you would be able to specify columns styles. It is obvious that integers should be right-aligned on the screen and averages should be comma-aligned. However stockTStringGrid
does not have kind of GetCellStyle event, so you would need some advanced grid derivative to add it. It is left as your home-task..
Now, it is unclear what you mean by those labels. I can guess, that you want to show there ranks according to both your two clans combined positions. The externals labels are a bad idea for few reasons.
FindComponent
is not too fast. Okay, you may find them once, cache inarray of TLabel
and be done. But why bother with extra workarounds?So, i think they should be INSIDE the row. If you want to highlight them - use bold font, or coloured, or large, or whatever inside the grid.
You correctly shown that some clans might have the same results and share the rank.
Before continuing you, as a JS user, have to notice a basis difference between
record
andclass
datatypes.record
is operated by value whileclass
is operated by reference. That means for class instances and variables you have to manually allocate memory for new elements and to dispose it for no longer used ones. Since class variable is a reference to some anonymous class instance(data). Hence the different containers of class-type elements can point to the single real element(data, instance), providing for easy data changing and cheaper sorting. Then for record instances (and record variable IS record data) you don't care about memory allocation and life times, yet would have copying data between different record instances, and if you change the one instance, to apply it to other containers you would have to copy it back. This difference is very visible infor element in container
loops, whether we can changeelement.field
or not.So let us have few more data structures for sorting and calculating. For example
We'll have then modification for the data dumper:
Another approach would be to keep ranks externally using something like
This approach is more extensible (allows in different window "attach" different extended data to the clans), but would require much more boilerplate. If you liek it more, your homework would be to implement it.