84

I have a String array kinda like this:

// icon, category, tool
String[,] subButtonData = new String[,]
{
    {"graphics/gui/brushsizeplus_icon", "Draw", "DrawBrushPlus"},
    {"graphics/gui/brushsizeminus_icon", "Draw", "DrawBrushMinus"},
    {"graphics/gui/freedraw_icon", "Draw", "DrawFree"},
    {"graphics/gui/linedraw_icon", "Draw", "DrawLine"},
    {"graphics/gui/rectangledraw_icon", "Draw", "DrawRectangle"},
    {"graphics/gui/ellipsedraw_icon", "Draw", "DrawEllipse"},
    {"graphics/gui/brushsizeplus_icon", "Brusher", "BrusherBrushPlus"},
    {"graphics/gui/brushsizeminus_icon", "Brusher", "BrusherBrushMinus"},
    {"graphics/gui/brushsizeplus_icon", "Text", "TextBrushPlus"},
    {"graphics/gui/brushsizeminus_icon", "Text", "TextBrushMinus"},
};

Then I populate a List<Button> with my Button Type named mainButtons

This is how I query for grouping for Category:

var categories = from b in mainButtons
                 group b by b.category into g
                 select new { Category = g.Key, Buttons = g };

How can I select the first item of each group in my main List? (without iterating each and adding to another List?)

abatishchev
  • 98,240
  • 88
  • 296
  • 433
JML
  • 951
  • 1
  • 7
  • 11

4 Answers4

107
var result = list.GroupBy(x => x.Category).Select(x => x.First())
Oscar Fraxedas
  • 4,467
  • 3
  • 27
  • 32
  • 8
    This will not work as of EF 6.0 if you try to do any other operations after the first query (such as select a subset of columns) - you need to use `x.FirstOrDefault`, otherwise you will get the 'First can only be used as the final operation exception' – Ben Jun 25 '18 at 01:22
81

See LINQ: How to get the latest/last record with a group by clause

var firstItemsInGroup = from b in mainButtons
                 group b by b.category into g
select g.First();

I assume that mainButtons are already sorted correctly.

If you need to specify custom sort order, use OrderBy override with Comparer.

var firstsByCompareInGroups = from p in rows
        group p by p.ID into grp
        select grp.OrderBy(a => a, new CompareRows()).First();

See an example in my post "Select First Row In Group using Custom Comparer"

Stephan
  • 4,187
  • 1
  • 21
  • 33
Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
  • 1
    Thanks I found this helpful. I would only suggest using FirstOrDefault instead of First to avoid possible runtime errors. – SendETHToThisAddress Sep 28 '20 at 02:17
  • @technoman23, it depends on your data. If you know that array/list has at least one element, First is more appropriate and if unexpected runtime exception happens, it is good(fail early). If your array/list can be empty, you should use FirstOrDefault and check for null the returned variable. – Michael Freidgeim Sep 28 '20 at 11:53
  • It works fine with IEnumerable, but How to Apply this on IQueryable ?! with IQueryable it gave me an error : System.InvalidOperationException .... .OrderByDescending(a => a.Amount)' could not be translated – Ahmed Abd Elmoniem Nov 13 '20 at 12:52
  • @AhmedAbdElmoniem, I suggest you to start a new question, describe your problem with more details, including your code example and link provider that you are using (e.g. EntityFramework). You are welcome to refer to this question. New questions are visible by many people, who can help – Michael Freidgeim Nov 13 '20 at 13:00
  • 1
    Mr. @MichaelFreidgeim, thanks for your advice, I posted the question here: https://stackoverflow.com/questions/64834181/how-to-use-linq-or-lambda-with-iqueryable-to-groupby-and-get-the-first-last-reco – Ahmed Abd Elmoniem Nov 14 '20 at 13:05
  • You can find a similar example in method syntax @ https://stackoverflow.com/questions/9132964/linq-group-by-order-by see answer Amy B. It would probably be something (depending on the EF-versions used) like var firstsByCompareInGroup = mainButtons.AsQueryable().GroupBy(field => new { field.Category}).Select(grouping => grouping.OrderBy(column => column.tool).First()); [I added in an ordering for fun ;-)]. For those who might have found this comment useful, plz give Amy B. an upvote, see link. – qqtf May 11 '23 at 00:01
30
var results = list.GroupBy(x => x.Category)
            .Select(g => g.OrderBy(x => x.SortByProp).FirstOrDefault());

For those wondering how to do this for groups that are not necessarily sorted correctly, here's an expansion of this answer that uses method syntax to customize the sort order of each group and hence get the desired record from each.

Note: If you're using LINQ-to-Entities you will get a runtime exception if you use First() instead of FirstOrDefault() here as the former can only be used as a final query operation.

ihake
  • 1,741
  • 1
  • 17
  • 29
5

First of all, I wouldn't use a multi-dimensional array. Only ever seen bad things come of it.

Set up your variable like this:

IEnumerable<IEnumerable<string>> data = new[] {
    new[]{"...", "...", "..."},
    ... etc ...
};

Then you'd simply go:

var firsts = data.Select(x => x.FirstOrDefault()).Where(x => x != null); 

The Where makes sure it prunes any nulls if you have an empty list as an item inside.

Alternatively you can implement it as:

string[][] = new[] {
    new[]{"...","...","..."},
    new[]{"...","...","..."},
    ... etc ...
};

This could be used similarly to a [x,y] array but it's used like this: [x][y]

Aren
  • 54,668
  • 9
  • 68
  • 101
  • If you're on .Net 4 life could even be easier by using `IEnumerable>` so you can ensure that you wil always have three items. For earlier .Net versions it may even be worth the effort to make your own Tuple class (or pick it from the internet, can't miss). – Gert Arnold Aug 07 '11 at 21:06
  • I was really excited about Tuples in .NET 4, but I've found as I've used them the verbosity has made them more trouble than they're worth. Especially when you have complex tuples: `Tuple, IDictionary, int>` duplicated a couple places in your code gets old fast. – Aren Aug 08 '11 at 15:37
  • 5
    You're quite right as long as you have to spell out your types. In many (most?) cases `Tuple.Create` does a perfect job because c# infers the types for you. Nevertheless, I use Tuples sparingly, because they are very non nondescript (what is Item1???). In public APIs they should be prohibited by law. – Gert Arnold Aug 08 '11 at 17:21