Get item index of a list within Select statement - c#

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++
};

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

How to Add Rownum to GroupBy Linq

I have a complex LINQ Query to extract Top students in my university. Here is the query :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm).Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
This Query give me top n students based on 4 parameters and on their TotalAverageTillTerm.
Now I want to add rownum for each group to simulate Total rank, for example Output is :
Now I want to Add TotalRank as rownumber like Sql. In the picture X1=1,X2=2,X3=3 and Y1=1,Y2=2,Y3=3
If I want to reduce problem. I only work on one group. Code Like this :
resultgroup = query.GroupBy(st => new
{
st.Student.StudyLevelId
}, st => st, (key, g) => new
{
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
list was a List of student but I see no sign of student having a rank property so I wrapped it into a annonimous type with rank.
var query = Db.Students.AsNoTracking().Where(...).AsEnumerable();
var resultgroup = query.GroupBy(st => new {
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
})
.SelectMany( g =>
g.OrderByDescending(x =>x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x,i) => new {
CourseStudyId = g.Key.CourseStudyId,
EntranceTermId = g.Key.EntranceTermId,
StudyingModeId = g.Key.StudyingModeId,
StudyLevelId = g.Key.StudyLevelId,
Rank = i+1
//studentPorperty = x.Prop1,
})
)
.AsQueryable();
Do you mean :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x, i) => new { Item = x, TotalRank = i /* item number inside group */}),
StudentsInGroupCount = g.Count() // count group this items
}).SelectMany(q => q).AsQueryable();
To see the results :
foreach (var item in resultgroup.ToList())
{
item.list.ForEach(s => Console.WriteLine(s.TotalRank));
}

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()});

Retrieve n items from each group in linq

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();

Group by and Count on List

I have a list of model class with some fields in it,I want to apply group by and count on the base of ID on it,in my lst I have data like this
lst[0]=Id=10,Name="user1",Gender="Male",Course="course1";
lst[1]=Id=10,Name="user1",Gender="Male",Course="course2";
lst[2]=Id=10,Name="user1",Gender="Male",Course="course3";
lst[3]=Id=11,Name="user2",Gender="Male",Course="course4";
I want to count the course with group by on Id.
This is how I am doing
var result = lst.GroupBy(n => n.Id).Select(c => new { Key = c.Key, total = c.Count()});
But after applying this I am not getting any value in result variable(ie result.Name or result[0].Name)
How can I apply group by and count in this list so I get the list data after applying the group by too.
Having used GroupBy you have projected (Select(..)) to an anonymous object which throws away the selected items in the group; your result will be an IEnumerable<anonymous> of items with properties Key and total
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count()});
If you want to keep the selected items, you need to include this in your projection
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count(), Items = c});
foreach(var r in result)
{
Console.WriteLine("Key: {0} Count:{1}",r.Key,r.total)
foreach(var i in r.Items)
Console.WriteLine(i.Name);
}
The reason is because you are grouping by Id and just provinding an anonymous object with Key (which is the Id) and a Count. If you want to read the values, each item of the result group set will have the Count and the values where you can loop between them. For sample:
var result = lst.GroupBy(n => n.Id);
foreach (var g in result)
{
Console.WriteLine("For group {0} the total is {1}.", g.Key, g.Count());
foreach(var item in g)
{
Console.WriteLine("Name: {0}", item.Name);
}
}
All you have to do is call ToList or ToArray.
var result = lst.GroupBy(n => n.Id)
.Select(c => new
{
Key = c.Key,
total = c.Count(),
Gender = c.First().Gender
}).ToList();
Or alternativly - to avoid multiple iterations of the same enumeration:
var result = lst.GroupBy(n => n.Id)
.Select(c =>
{
var firstEl = c.First();
return new {
Key = c.Key,
total = c.Count(),
Gender = firstEl.Gender,
Name = firstEl.Name,
Course = firstEl.Course
}
}).ToList();
Select will only return a query which is deferredly executed. This means you get the result when you actually iterate on the enumeration, which is enforeced by calling one of the mentioned methods.

Categories