Retrieve n items from each group in linq - c#

I search more on this site for "get 4 top item from each group", but there are many topic about get first item from each group like this
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId, (key, g) => g.OrderBy(e => e.PersianName)
.Take(4).ToList());
or
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId).Select(g => new { pname = g.Key, count = g.Count() });
but return only first item from each group. How can I change the code to get 4 items from each group?

Try this:
var rr = db.Products.GroupBy(x => x.ProductSubTypeCategoryId).Select(g => new { GroupName = g.Key, Items = g.Take(4).ToList() });
This should give you an anonymous object with a GroupName property that returns the ProductSubTypeCategoryId and an Items property that returns a list of up to 4 items for each group.

Try something like this with SelectMany()
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId)
.SelectMany(g => g.OrderBy(e => e.PersianName).Take(4))
.ToList();

Related

Group by in Linq with condition

I want to group by a table with Order Id but if one of price is negative don’t group by and brings all rows in output
I use below code but group by all order id
tblResult = tblResult.AsEnumerable().GroupBy(r => new { orderId = r["OrderID"] }).Select(g =>
{
var row = tblResult.NewRow();
row["Order ID"] = g.Key.orderId;
row["Price"] = g.Sum(r => float.Parse(r.Field<string>("Price"))).ToString();
return row;
}).CopyToDataTable();
You can create your condition in grouping, the tricky part is the result would be a list for those with negative prices and single item for those without it. if we also make single items as list then SelectMany() shoud do what you want:
var result = list.GroupBy(x => x.Id)
.SelectMany(g => g.Any(x => x.Price < 0)?
g.ToList():
new List<Order> { new Order { Id = g.Key, Price = g.Sum(grp => grp.Price)}});
LIVE DEMO

Use SelectMany to extract value from list and pass to groups

I have the following query where I use SelectMany on a list of lists and then GroupBy 'Name' and the 'Count' of the Lists and store the values in 'Name' and 'Count'.
var groups = originalList.SelectMany(fullList => fullList.ListOfItems, (fullList, details) => new { fullList.Name, fullList.ListOfItems })
.GroupBy(x => x.Name,
x => x.ListOfItems.Count())
.Select(g => new { Name = g.Key, Count = g.Count()});
//Do something with results
foreach (var item in groups)
{
var name = item.Name;
var count = item.Count;
}
Now there is another paramater from the originalList that I want to pass through to the resulting groups lets call it fullList.SomeOtherValue.
How can I modify this above so that it is passed through also?
I want to end up withthis:
foreach (var item in groups)
{
var name = item.Name;
var count = item.Count;
var someother = item.SomeOtherValue; <-- I want this as well
}
it sounds like something like this should work:
originalList.SelectMany(fullList => fullList.ListOfItems)
.GroupBy(x => new { x.Name, x.SomeOtherValue})
.Select(g => new { g.Key.Name, g.Key.SomeOtherValue, Count = g.Count()});

Get item index of a list within Select statement

I need to index the item list with its position after grouping
var result = from i in items
group i by i.name into g
select new { groupname = g.Key,
index = //need to get the index of the item
};
How to get the item index of a list using linq/lambda?
I'm not 100% sure what you're trying to achieve, but I would definitely advice to use methods instead of syntax-based query.
var results = items.GroupBy(x => x.name)
.Select((g, i) => new { product = g.Key, index = i });
Or if you'd like to get indexes from source lift for all items within every group:
var results = items.Select((x, i) => new { x, i })
.GroupBy(x => x.x.name)
.Select(g => new {
product = g.Key,
indexes = g.Select(x => x.i).ToList()
});
var idx = 0;
var result = from i in items
group i by i.name into g
select new { product = g.Key,
index = idx++
};

Group By using Select statement in Linq

var query = from i in SFC.Supplies_ReceiveTrans
orderby i.Poprctnm descending
select new { RR = i.Poprctnm };
Result:
RR-01,
RR-01,
RR-02,
RR-02,
RR-02,
RR-TEST,
RR-TEST,
How do i group RR in this kind of statement
Result:
RR-01,
RR-02,
RR-TEST
just a few modification to ask if is it possible to do this one or what you have in your mind? Sorry for asking too much just really interested in learning more on linq.. how do i convert it into string coz its showing true or false.. boolean statement
var query = SFC.Supplies_ReceiveTrans.Select(s =>
s.Poprctnm.StartsWith(p))
.Distinct()
.OrderBy(p => p)
.Select(p => new { RR = p })
.Take(10);
You can use Distinct or GroupBy methods in this case
var query = SFC.Supplies_ReceiveTrans.Select(s=> s.Poprctnm)
.Distinct()
.OrderByDescending(p => p)
.Select(p=> new { RR = p });
if you use OrderByDescending then the result will be
RR-TEST
RR-02
RR-01
But I think you want OrderBy then the result will be
RR-01
RR-02
RR-TEST
So try below
var query = SFC.Supplies_ReceiveTrans.Select(s=> s.Poprctnm)
.Distinct()
.OrderBy(p => p)
.Select(p=> new { RR = p });
var query = SFC.Supplies_ReceiveTrans
.GroupBy(x=>x.Poprctnm)
.Select(g=>g.First())
.OrderByDescending(x=>x.Poprctnm)
.Select(x=>new { RR = x.Poprctnm });
If you want to get result as group:
var query = SFC.Supplies_ReceiveTrans
.GroupBy(x=>x.Poprctnm)
.OrderByDescending(g=>g.Key);
var result = SFC.Supplies_ReceiveTrans
.Select(x => new { RR = x.Poprctnm })
.Distinct()
.OrderByDescending(x => x.Poprctnm);
Looks like you need Distinct here, not group
var query = SFC.Supplies_ReceiveTrans
.Select(x => new {RR = i.Poprctnm})
.Distinct()
.OrderByDescending(i => i);

Search list and order List by value max found c#

i like to confess that i am weak in LINQ.
i have list with data. i want to search list fist by given value and then sort data by max occurance means which comes maximum time in rows.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
if i search & sort list with data like "dog cat" then output will be like
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=Cat
ID=2,Title=dog
ID=6,Title=dog
all cat will come first because this cat keyword found maximum time in all the rows and then dog found maximum time.
this below data will not come because it is not in search term
ID=3,Title=Tiger
looking for solution. thanks
UPDATE PORTION CODE
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=2,Title="Excavator JCB - ECU P/N: 728/35700"},
new SearchResult(){ID=3,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=4,Title="JCB Excavator - ECU P/N: 728/35700"},
new SearchResult(){ID=5,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "Geo", "JCB" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
dataGridView2.DataSource = completeList.ToList();
i try to your logic in another apps but it is not working. according to logic three rows first come with GEO keyword and then next 2 rows comes with JCB and then unmatched rest comes. what i need to change in ur code. please help. thanks
This will filter your list and group it by Title, sorting the groups by their size.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "cat", "dog" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
foreach (var group in result)
foreach (var element in group)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output:
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
If you don't care about the actual grouping, just can flatten the list of groups with SelectMany.
(Note that this code will ignore the case of Title. I don't know if this is what you want or if it is a typo in code: you are using cat and Cat, and in your output it is only Cat, but dog is not capitalized.)
Edit:
To get the unmatched items, you can use Except:
var unmatched = list.Except(result.SelectMany(m => m)); // beware! contains the tiger!
Edit 2:
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
foreach (var element in completeList)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
ID=3,Title=Tiger
Edit 3
var result = from searchResult in list
let key_string = to_search.FirstOrDefault(ts => searchResult.Title.ToLower().Contains(ts.ToLower()))
group searchResult by key_string into Group
orderby Group.Count() descending
select Group;
IEnumerable<string> pets = new[] { "Cat", "dog", "Tiger", "Cat", "cat", "dog" };
var test = pets
.Where(p=>p.ToUpperInvariant() == "CAT" || p.ToUpperInvariant() == "DOG")
.GroupBy(p => p.ToUpperInvariant())
.OrderByDescending(g => g.Count())
.SelectMany(p => p);
foreach (string pet in test)
Console.WriteLine(pet);
I think the following should do the trick.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.Where(i => searchText.Contains(i.Item.Title))
.OrderByDescending(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()
Update
If you want unmatched data at the end, you need to add another sorting property in the anonymous object.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Matched = searchText.Contains(i.Item.Title.ToUpper()),
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.OrderByDescending(i => i.Matched)
.ThenBy(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()

Categories