Row_number over (Partition by xxx) in Linq?

I have a DataTable which has a structure and data:

id |   inst   |   name
------------------------
 1 |  guitar  |  john
 2 |  guitar  |  george
 3 |  guitar  |  paul
 4 |  drums   |  ringo
 5 |  drums   |  pete

I can retrieve the records via:

IEnumerable <Beatle>...

class Beatle
{
  int id;
  string inst;
  string name;
}

I'd like to get the internal order of those who play the different instruments. In MSSQL I'd use

SELECT 
    *
    ,Row_Number() OVER (PARTITION BY inst ORDER BY id) AS rn
FROM Beatles

This query returns

id |   inst   |   name  | rn
-----------------------------
 1 |  guitar  |  john   | 1
 2 |  guitar  |  george | 2
 3 |  guitar  |  paul   | 3
 4 |  drums   |  ringo  | 1
 5 |  drums   |  pete   | 2

Question:
How can I do that in Linq?


Try this one liner:

var o = beatles
    .OrderBy( x => x.id )
    .GroupBy( x => x.inst )
    .Select( group => new { Group = group, Count = group.Count() } )
    .SelectMany( groupWithCount =>
        groupWithCount.Group.Select( b => b)
        .Zip(
            Enumerable.Range( 1, groupWithCount.Count ),
            ( j, i ) => new { j.inst, j.name, RowNumber = i }
        )
    );

foreach (var i in o)
{
    Console.WriteLine( "{0} {1} {2}", i.inst, i.name, i.RowNumber );
}

Output:

Guitar John 1
Guitar George 2
Guitar Paul 3
drums Ringo 1
drums Pete 2

B"H

I know this is old. But why isn't the solution simply?

var o = beatles.GroupBy(x => x.inst)
               .SelectMany(g =>
                   g.Select((j, i) => new { j.inst, j.name, rn = i + 1 })
               );

Another idea is using a view, if possible.


As @The_Smallest points out, row number is not supported by LINQ. Here's how you can get what you're looking for, though:

var grouped = beatles.OrderBy( x => x.id )
  .ToList()   // required because SelectMany below doesn't evaluate to SQL
  .GroupBy( x => x.inst );
var rns = grouped.ToDictionary( x => x.Key, x => 1 );
var result = grouped
  .SelectMany( x => x.Select( 
    y => new { inst = y.inst, name = y.name, rn = rns[y.inst]++ } ) );