Click here to Skip to main content
15,885,365 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
hello everyone. 
I am trying to rate a group of numbers in a datagrid containing two columns. I want the cells of the Coulmn B to have values as 1st, 2nd, 3rd etc, based on the values of Column A.

For example:

Col A            Col B
80                    2
305                   1
23                    5
43                    4
31                    3


What I have tried:

All I know is how to assign the result to the cells of Column B after its found

Dim iRow As Integer
        Dim cellA As Integer
        Dim cellB As Integer

        For iRow = 0 To Me.DataGridView1.RowCount - 1

            Me.DataGridView1.Rows(iRow).Cells(1).Value = cellA
        Next
        iRow = irow + 1
Posted
Updated 9-Jun-23 12:42pm
Comments
Richard MacCutchan 11-Jun-18 11:19am    
If you sort the grid on column A in descending order then you can set the rankings in column B with s simple counter.
SamuelDexter 11-Jun-18 11:46am    
Off course thats, the simplest way to go. I agree but I for some reasons decided to disabled column header sorting after all because using the common

Datagridview1.Sort(Datagridview1.Columns(1), System.ComponentModel.ListSortDirection.Ascending)

has never worked for me; always errors :(

Richard MacCutchan 11-Jun-18 12:01pm    
"always errors :("
Then please edit your question, show the code and explain exactly what errors.
SamuelDexter 11-Jun-18 13:08pm    
System.ArgumentException: 'Object must be of type String.'
Richard MacCutchan 11-Jun-18 13:38pm    
So what part of that do you not understand?

Something like this should work, and will correctly rank ties:
VB.NET
Dim cumulativeRank As Integer = 1
For Each group As IGrouping(Of Object, DataGridViewRow) In DataGridView1.Rows.Cast(Of DataGridViewRow)().GroupBy(Function (r) r.Cells(0).Value).OrderByDescending(Function (g) g.Key)
    Dim rank As Integer = cumulativeRank
    For Each row As DataGridViewRow In group
        row.Cells(1).Value = rank
        cumulativeRank += 1
    Next
Next

If you don't care about ties, then you don't need the grouping:
VB.NET
Dim rank As Integer = 1
For Each row As DataGridViewRow In DataGridView1.Rows.Cast(Of DataGridViewRow)().OrderByDescending(Function (r) r.Cells(0).Value)
    row.Cells(1).Value = rank
    rank += 1
Next
 
Share this answer
 
Comments
Maciej Los 12-Jun-18 17:13pm    
On the first look: when datagridview stores unique values, your code is OK, but when there are duplicates your "ranking" method will return wrong result. ;(
By the way - i was able to understand what OP want to achieve only after reading your answer.
A 4!
Richard Deeming 13-Jun-18 5:55am    
That's why there are two versions.

* Use the second version if you don't care about ties - similar to SQL's ROW_NUMBER function.
* Use the first version if you do care about ties, and want them ranked properly - similar to SQL's RANK function.

If you want something similar to DENSE_RANK, where ties don't leave gaps, then it would be trivial to adapt the first version:
Dim rank As Integer = 1
For Each group As IGrouping(Of Object, DataGridViewRow) In DataGridView1.Rows.Cast(Of DataGridViewRow)().GroupBy(Function (r) r.Cells(0).Value).OrderByDescending(Function (g) g.Key)
    For Each row As DataGridViewRow In group
        row.Cells(1).Value = rank
    Next
    
    rank += 1
Next
Maciej Los 13-Jun-18 6:02am    
Oh... i see. It was late yesterday...
Upvoted!
In addition to Richard Deeming[^]'s solution and my comment to His solution...

I'd suggest to use Dictionary[^] object when there are rows with duplicate values in ColA

VB.NET
Dim dgv As DataGridView = Me.DataGridView1
Dim oDict As Dictionary(Of Integer, Integer) = dgv.Rows().Cast(Of DataGridViewRow) _
	.OrderByDescending(Function(r) r.Cells("ColA").Value) _
	.GroupBy(Function(r) r.Cells("ColA").Value) _
	.Select(Function(grp, index) New KeyValuePair(Of Integer, Integer)(grp.Key, index+1)) _
	.ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.Value)
	
For Each dgr As DataGridViewRow In dgv.Rows
	dgr.Cells(1).Value = oDict(dgr.Cells(0).Value)
Next


Sample output:
80	2
305	1
23	5
43	3
31	4
80	2
305	1
23	5

As you can see, 305 gets 1. rank, 80 - 2. and so on...

Good luck!

[EDIT]
As to the Richard's comment to my answer - small improvement (still using Dictionary object):

VB.NET
Dim oDict As Dictionary(Of Integer, Tuple(Of Integer, Integer)) = dgv.Rows().Cast(Of DataGridViewRow) _
	.GroupBy(Function(r) r.Cells("ColA").Value) _
	.OrderByDescending(Function(g) g.Key) _
	.Select(Function(grp, index) New KeyValuePair(Of Integer, Tuple(Of Integer, Integer))(grp.Key, Tuple.Create(index+1, grp.Count-1))) _
	.ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.Value)

For Each dgr As DataGridViewRow In dgv.Rows
	Dim cumulative As Integer = oDict.TakeWhile(Function(x) x.Key>dgr.Cells(0).Value).Sum(Function(x) x.Value.Item2)
	dgr.Cells(1).Value = oDict(dgr.Cells(0).Value).Item1  + cumulative
Next


Result:
80	3
305	1
23	7
43	5
31	6
80	3
305	1
23	7
 
Share this answer
 
v3
Comments
Richard Deeming 13-Jun-18 5:57am    
A proper ranking leaves gaps for ties. :)

Imagine a race, where the first two people cross the finish line at the same time - they are joint first. The next person to cross the line is the third person to cross the line, and is therefore ranked third.
Maciej Los 13-Jun-18 6:06am    
Agree. A statement about 1st, 2nd, etc. pointed me out wrong direction ;)
[EDIT]
Updated! Thanks for your valuable comment.
Cheers,
Maciej
    Dim oDict As Dictionary(Of Integer, Tuple(Of Integer, Integer)) = DataGridView1.Rows().Cast(Of DataGridViewRow).GroupBy(Function(r) r.Cells(10).Value).OrderByDescending(Function(g) g.Key) _
.Select(Function(grp, index) New KeyValuePair(Of Integer, Tuple(Of Integer, Integer))(grp.Key, Tuple.Create(index + 1, grp.Count - 1))) _
.ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.Value)

    For Each dgr As DataGridViewRow In DataGridView1.Rows
        Dim cumulative As Integer = oDict.TakeWhile(Function(x) x.Key > dgr.Cells(10).Value).Sum(Function(x) x.Value.Item2)
        dgr.Cells(13).Value = oDict(dgr.Cells(10).Value).Item1 + cumulative
    Next



81.
Marks	Grade     Remark            Position
68.2	4	      High Average	    4
63.2	4	      High Average	    5
56.2	5	      Average	        6
39.5	8	      Lower	            10
57.5	5	      Average	        7
68.2	4	      High Average	    4
68.5	4	      High Average	    1
50.3	6	      Low Average	    8
68.5	4	      High Average	    1
43.7	7	      Low	            9
68.3	4	      High Average	    3
35.5	8	      Lower	            11

i had an error, it keeps on telling me "the object must be double"
<pre>81.
the results should be

Marks	Grade     Remark            Position
68.2	4	      High Average	    4
63.2	4	      High Average	    5
56.2	5	      Average	        6
39.5	8	      Lower	            10
57.5	5	      Average	        7
68.2	4	      High Average	    4
68.5	4	      High Average	    1
50.3	6	      Low Average	    8
68.5	4	      High Average	    1
43.7	7	      Low	            9
68.3	4	      High Average	    3
35.5	8	      Lower	            11
 
Share this answer
 
Comments
Richard Deeming 12-Jun-23 4:51am    
Your question is not a "solution" to someone else's question.

If you want to ask a question, then ASK A QUESTION[^]. But you're going to have to provide a lot more information than you have here if you want anyone to be able to help you.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900