I've a class like this:
public class ReportList
{
public int? ProjectId { get; set; }
public string Name { get; set; }
public string ProjectName { get; set; }
public int LevelId { get; set; }
public int Minutes { get; set; }
public int Hours { get; set; }
public int ExtraMinutes { get; set; }
public int ExtraHours { get; set; }
}
And I've list of this class
List<ReportList> repList = new List<ReportList>();
I've added items to this list:
repList.Add(new ReportList(1 , "a" , "project a", 2, 30, 1, 45, 2));
repList.Add(new ReportList(1 , "b" , "project a", 2, 30, 2, 15, 1));
repList.Add(new ReportList(1 , "c" , "project a", 2, 0, 3, 10, 0));
I want to combine this list items into one item by sum minutes and hours. So the list should be like this:
{1, "a", "project a", 2, 60, 6, 70, 3};
What can I do?
Use GroupBy extension method on ProjectId,ProjectName and LevelId fields.
var results = repList.GroupBy(x=> new {x.ProjectId, x.ProjectName, LevelId })
.Select(x=> new // or create new ReportList object.
{
ProjectId = x.Key.ProjectId,
ProjectName = x.Key.ProjectName,
Name = x.First().Name, // I assume it is first one as per example, modify if you want.
LevelId = x.Key.LevelId,
Minutes = x.Sum(s=>s.Minutes),
Hours = x.Sum(s=>s.Hours ),
ExtraMinutes = x.Sum(s=>s.ExtraMinutes ),
ExtraHours = x.Sum(s=>s.ExtraHours)
})
.ToList() ;
If you want more optimized version of answer posted by user Hari Prasad you could use following;
int minuteSum = 0;
int hoursSum = 0;
int extraMinutesSum = 0;
int extraHoursSum = 0;
foreach (var report in repList)
{
minuteSum += report.Minutes;
hoursSum += report.Hours;
extraMinutesSum += report.ExtraMinutes;
extraHoursSum += report.ExtraHours;
}
var firstItemInRepList = repList.First();
var result = new ReportList(firstItemInRepList.ProjectId,
firstItemInRepList.Name,
firstItemInRepList.ProjectName,
firstItemInRepList.LevelId,
minuteSum,
hoursSum,
extraMinutesSum,
extraHoursSum);
I know its more crude version but it will take less cpu.
var results = repList
.GroupBy(x => "all")
.Select(x=> new {
ProjectId = x.First().ProjectId,
Name = x.First().Name,
ProjectName = x.First().ProjectName,
LevelId = x.First().LevelId,
Minutes = x.Sum(s=>s.Minutes),
Hours = x.Sum(s=>s.Hours ),
ExtraMinutes = x.Sum(s=>s.ExtraMinutes),
ExtraHours = x.Sum(s=>s.ExtraHours)
});
I am refering answer posted by user Hari Prasad, but as per question requirement we need to apply groupby only on ProjectId i guess.
Please refer below code.
var processedResult = repList.GroupBy(x => x.ProjectId)
.Select(x => new ReportList
{
ProjectId = x.Key,
ProjectName = x.First().ProjectName, //As per your example it is first row data
Name = x.First().Name, //As per your example it is first row data
LevelId = x.First().LevelId,
Minutes = x.Sum(s => s.Minutes),
Hours = x.Sum(s => s.Hours),
ExtraMinutes = x.Sum(s => s.ExtraMinutes),
ExtraHours = x.Sum(s => s.ExtraHours)
}).ToList();
Related
I have a requirement to group a list of class objects based on another list inside the Object.
class TransactionObject
{
public int ProjectId { get; set; }
public string uniqueId { get; set; }
public string OrgNumber { get; set; }
public string OrgName { get; set; }
public List<TransactionValue> TransactionValue{ get; set; } = new List<TransactionValue>();
public class TransactionValue
{
public DateTime TrnDate { get; set; }
public decimal EURTrans { get; set; }
public decimal LocaTrans { get; set; }
public decimal BrokeragePercentage { get; set; }
}
}
Now on this class, I have created a list of objects.
var TransactionList = new List<TransactionObject>();
I want to get the list of Unique ProjectsIdm OrgName and The sum of EUR Trans, Local Trans, based on a Group by on TrnDate.
Example:
ProjectId OrgName Trn Date EUR Trns Local Trns
543332 Organization 1 1-Jan-22 100 150
543332 Organization 1 1-Jan-22 150 20
I Need :
Sorry MY BAD I edited the correct output i require
ProjectId OrgName Trn Date EUR Trns Local Trns
543332 Organization 1 1-Jan-22 250 170
What I tried :
List<TransactionObject> result = TransactionList .GroupBy (g => new {
g.HoldingName, g.TransactionValues.First().TrntDate })
.Select(g => g.First())
.ToList();
I tried this, but it doesn't help me with the Sum of the columns, I am good in Java but new to C# please help me out. I have already crossed my deadline on this object.
I think it could be beneficial to split the operation into 2 stages.
var flattened = TransactionList
.SelectMany(
collectionSelector: o => o.Transactions,
resultSelector: (fullObject, transaction) => new { fullObject.ProjectId, fullObject.OrgName, Transaction = transaction });
var grouped = flattened
.GroupBy (t => new {t.ProjectId, t.OrgName, t.Transaction.TrnDate })
.Select( g => new
{
g.Key.ProjectId,
g.Key.OrgName,
g.Key.TrnDate,
SumEURTrans = g.Sum( t => t.Transaction.EURTrans),
SumLocaTrans = g.Sum( t => t.Transaction.LocaTrans)
})
.ToList();
foreach (var t in grouped)
{
Console.WriteLine($"{t.ProjectId}\t{t.OrgName}\t{t.TrnDate}\t{t.SumEURTrans}\t{t.SumLocaTrans}");
}
This produces
543332 Organization 1 1-Jan-22 250 170
543332 Organization 1 2-Jan-22 450 470
543333 Organization 1 1-Jan-22 250 170
for the example input of
var TransactionList = new [] {
new TransactionObject
{
ProjectId = 543332,
OrgName = "Organization 1",
Transactions = new List<TransactionObject.TransactionValue>
{
new TransactionObject.TransactionValue
{
TrnDate = "1-Jan-22",
EURTrans = 100,
LocaTrans = 150
},
new TransactionObject.TransactionValue
{
TrnDate = "1-Jan-22",
EURTrans = 150,
LocaTrans = 20
}
,new TransactionObject.TransactionValue
{
TrnDate = "2-Jan-22",
EURTrans = 200,
LocaTrans = 250
},
new TransactionObject.TransactionValue
{
TrnDate = "2-Jan-22",
EURTrans = 250,
LocaTrans = 220
}
}
},
new TransactionObject
{
ProjectId = 543333,
OrgName = "Organization 1",
Transactions = new List<TransactionObject.TransactionValue>
{
new TransactionObject.TransactionValue
{
TrnDate = "1-Jan-22",
EURTrans = 100,
LocaTrans = 150
},
new TransactionObject.TransactionValue
{
TrnDate = "1-Jan-22",
EURTrans = 150,
LocaTrans = 20
}
}
}
};
This will group by {t.ProjectId, t.OrgName, t.Transaction.TrnDate} across all objects and you need to decide if that's what you want (an example alternative being grouping only within each TransactionObject).
If I understand you right - you can do it this way in one run:
public void GroupTransactions(TransactionObject[] transactionObjects)
{
var results = transactionObjects
// build a flat list of values to have both transactionObject and value for every transactionValue
.SelectMany(obj => obj.TransactionValues.Select(value => (obj, value)))
.GroupBy(tuple => new {tuple.obj.ProjectId, tuple.obj.OrgName, tuple.value.TrnDate})
// Get sums for every group. You can use '.Aggregate()' method instead of this custom GetSum but it seems less readable to me.
.Select(group => (group.Key, sum: GetSum(group.Select(tuple => tuple.value))))
.ToArray();
foreach (var result in results)
Console.WriteLine($"{result.Key.ProjectId} {result.Key.OrgName} {result.Key.TrnDate} {result.sum.euro} {result.sum.local}");
}
private static (decimal euro, decimal local) GetSum(IEnumerable<TransactionObject.TransactionValue> values)
{
decimal euro = 0, local = 0;
foreach (var value in values)
{
euro += value.EURTrans;
local += value.LocaTrans;
}
return (euro, local);
}
I'm trying to build a summary query that i will be using for statistics.
i have a dataTable with the folowing columns (approx 18000 rows) :
Artist / Album / file_path (one for each song) / rating /
each artist has 1 or several album with has songs and each songs have a rating
I want to have the following result :
For each artist ID (more reliable than the artist name), the total number of albums, the total number of songs, and the total number of ratings equal to 5.
Artist x / #album / #songs / #rating = 5 / song.first() //in song.first i have access to the file path, it can be any file path from the artist hence the first one.
I've been pulling my hair for several hours now and i cannot manage to get the # of albums per artist :( This is what i've been trying so far :
i have a Class for the query :
public class art_detail
{
public string artiste { get; set; }
public string fp { get; set; } // the file_path
public int nbr_album { get; set; }
public int nbr_song { get; set; }
public int nbr_rat5 { get; set; }
}
this is the query i came up to :
var result = from res in Globals.ds.Tables[0].AsEnumerable() // the table
.GroupBy(x => new { art = x.Field<int>("Artist_ID"), alb = x.Field<string>("album") })
.Select(x => new art_detail { artiste = x.Select(p =>p.Field<string>("artiste")).First(), fp = x.Select(p=>p.Field<string>("file_path")).First(), nbr_album = x.Key.alb.Count() })
.OrderBy(x => x.artiste)
select res;
The count is unfortunately completely wrong and i have no idea how to get the # of rating = 5 :(
Thanks for the help !
Edit :
Here is my query to make it work :
var table = Globals.ds.Tables[0].AsEnumerable();
var stats = table.GroupBy(x => x.Field<int>("Artist_ID"))
.Select(x => new art_detail
{
artiste = x.Select(p=>p.Field<string>("artiste")).First(),
nbr_album = x.Select(y => y.Field<string>("album")).Distinct().Count(),
fp = x.Select(y => y.Field<string>("file_path")).FirstOrDefault(),
nbr_song = x.Count(),
nbr_rat5 = x.Count(y => y.Field<int>("Rating") == 5)
});
Simpler than what i thought :)
Assuming a table whose schema matches this class:
public class Song
{
public string ArtistID { get; set; }
public string Album { get; set; }
public string FilePath { get; set; }
public int Rating { get; set; }
}
and given a LINQ source, you have the following query:
IQueryable<Song> table = /*insert source*/;
var stats = table.GroupBy(x => x.ArtistID);
.Select(x => new art_detail
{
artiste = x.Key,
nbr_album = x.Select(y => y.Album).Distinct().Count(),
nbr_song = x.Count(),
nbr_rat5 = x.Count(y => y.Rating == 5),
});
I used head compiled query as it seemed more understandable for me in this case:
Example model:
public class Artist
{
public string ArtistID { get; set; }
public string Album { get; set; }
public string FilePath { get; set; }
public int Rating { get; set; }
public int NumberOfSongs { get; set; }
}
Creating some dummy records for Usher and Beyonce:
//Usher
var artistOne = new Artist()
{
ArtistID = "Usher",
Album = "Lit",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 9
};
var artistTwo = new Artist()
{
ArtistID = "Usher",
Album = "Sick",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 11
};
var artistThree = new Artist()
{
ArtistID = "Usher",
Album = "Dope",
FilePath = "dummy/path/here",
Rating = 4,
NumberOfSongs = 14
};
//Beyonce
var artistFour = new Artist()
{
ArtistID = "Beyonce",
Album = "Hot",
FilePath = "dummy/path/here",
Rating = 5,
NumberOfSongs = 8
};
var artistFive = new Artist()
{
ArtistID = "Beyonce",
Album = "Fire",
FilePath = "dummy/path/here",
Rating = 4,
NumberOfSongs = 16
};
var listOfArtist = new List<Artist> { artistOne, artistTwo, artistThree, artistFour, artistFive };
Running query:
var result = from a in listOfArtist
where a.Rating == 5
group a by a.ArtistID into art
select new
{
artist = art.Key,
numberOfAlbums = art.Count(),
numberOfSongs = art.Sum(d => d.NumberOfSongs),
};
Results:
Hope this helps =)
I'm new to Stack Overflow, but tried to put as much information
I have following class structure
public class ItemEntity
{
public int ItemId { get; set; }
public int GroupId { get; set; }
public string GroupName { get; set; }
public DateTime ItemDate { get; set; }
public string Field1 { get; set; }
public string Filed2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public int Duration { get; set; }
}
public class MasterEntity
{
public ItemEntity Item { get; set; }
public List<int> ItemList { get; set; }
public List<int> GroupList { get; set; }
}
I am trying to group list of ItemEntity into MasterEntity. Grouping fileds are Field1,Field2 and Field3.
I have done the grouping so far like below
var items = new List<ItemEntity>
{
new ItemEntity
{
ItemId = 100,
GroupId = 1,
GroupName= "Group 1",
ItemDate = new DateTime(2018,10,17),
Duration = 7,
Field1 = "Item Name 1",
Filed2 = "aaa",
Field3= "bbb",
Field4= "abc"
},
new ItemEntity
{
ItemId = 150,
GroupId = 2,
GroupName= "Group 2",
ItemDate = new DateTime(2018,10,17),
Duration = 5,
Field1 = "Item Name 1",
Filed2 = "aaa",
Field3= "bbb",
Field4= "efg"
},
new ItemEntity
{
ItemId = 250,
GroupId = 3,
GroupName= "Group 3",
ItemDate = new DateTime(2018,10,15),
Duration = 7,
Field1 = "Item Name 1",
Filed2 = "aaa",
Field3= "bbb",
Field4= "xyz"
}
};
var group = items.GroupBy(g => new
{
g.Field1,
g.Filed2,
g.Field3
}).Select(s => new MasterEntity
{
Item = new ItemEntity
{
Field1 = s.Key.Field1,
Filed2 = s.Key.Filed2,
Field3 = s.Key.Field3
},
ItemList = s.Select(g => g.ItemId).ToList(),
GroupList = s.Select(g => g.GroupId).ToList()
}).ToList();
With in this group, I want to further split this by actual ItemDate and Duration so it looks like below
Basically, I want to split this group in to three in this case.
As only Group3 is having Date 15th to 17, it will be one group.
From 17th to 22nd Group1, Group2 and Group3 are same. so that will become another group.
And last only Group1 have 22nd to 24 so it become another group
Final grouped data to be like
G1
{
ItemEntity :{
ItemDate : 15/10/2018,
Duration : 2,
Field1 : "Item Name 1",
Filed2 : "aaa",
Field3 : "bbb",
},
ItemList: {250},
GroupList:{3}
}
,
G2
{
ItemEntity :{
ItemDate : 17/10/2018,
Duration : 5,
Field1 : "Item Name 1",
Filed2 : "aaa",
Field3 : "bbb",
},
ItemList: {100,150,250},
GroupList:{1,2,3}
}
,
G3
{
ItemEntity :{
ItemDate : 22/10/2018,
Duration : 2,
Field1 : "Item Name 1",
Filed2 : "aaa",
Field3 : "bbb",
},
ItemList: {100},
GroupList:{1}
}
This was pretty challenging. I used some convenient extension methods I already had to make it easier, and created a HashSet subclass that defaults to using SetEqual (.Net really needs some member equal collection classes built-in).
First, the class HashSetEq that implements equality when its members match:
public class HashSetEq<T> : HashSet<T>, IEquatable<HashSetEq<T>> {
private static readonly IEqualityComparer<HashSet<T>> SetEq = HashSet<T>.CreateSetComparer();
public override int GetHashCode() => SetEq.GetHashCode(this);
public override bool Equals(object obj) => obj != null && (obj is HashSetEq<T> hs) && this.Equals(hs);
public bool Equals(HashSetEq<T> other) => SetEq.Equals(this, other);
public HashSetEq(IEnumerable<T> src) : base(src) {
}
}
Now, some extensions to IEnumerable. One extension converts an IEnumerable to a HashSetEq for ease of creating a collection of keys. The other extension is a variation on GroupBy that groups while a predicate is true, based on an extension ScanPair that implements a pair-wise version of the APL Scan operator.
public static class IEnumerableExt {
public static HashSetEq<T> ToHashSetEq<T>(this IEnumerable<T> src) => new HashSetEq<T>(src);
// TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
// PrevKeyItem.Key = Previous Key
// PrevKeyItem.Value = Previous Item
// curItem = Current Item
// returns new Key
public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
}
public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
}
In order to group the spans of dates, I expanded my GroupBySequential based on GroupByWhile inline so I could group by sequential date runs and matching sets of GroupIds. GroupBySequential depends on an integer sequence, so I need a base Date to compute a day sequence number so I use the earliest date in all the items:
var baseDate = items.Min(i => i.ItemDate);
Now I can compute the answer.
For each group of items, I expand each item out across all the dates it covers, based on Duration, and associated each date with the original item:
var group = items.GroupBy(g => new {
g.Field1,
g.Filed2,
g.Field3
})
.Select(g => g.SelectMany(i => Enumerable.Range(0, i.Duration).Select(d => new { ItemDate = i.ItemDate.AddDays(d), i }))
Now that I have all the individual date+item, I can group them for each date.
.GroupBy(di => di.ItemDate)
And then group each date+items on the date and set of groups for that date and order by the date.
.GroupBy(dig => new { ItemDate = dig.Key, Groups = dig.Select(di => di.i.GroupId).ToHashSetEq() })
.OrderBy(ig => ig.Key.ItemDate)
With them ordered by date, I can group the sequential dates together (using the number of days from the baseDate) that have the same Groups.
.GroupByWhile((prevg, curg) => (int)(prevg.Key.ItemDate - baseDate).TotalDays + 1 == (int)(curg.Key.ItemDate - baseDate).TotalDays && prevg.Key.Groups.Equals(curg.Key.Groups))
Finally, I can extract the information from each sequential date group into a MasterEntity and make it the whole answer a List.
.Select(igg => new MasterEntity {
Item = new ItemEntity {
ItemDate = igg.First().Key.ItemDate,
Duration = igg.Count(),
Field1 = g.Key.Field1,
Filed2 = g.Key.Filed2,
Field3 = g.Key.Field3
},
ItemList = igg.First().First().Select(di => di.i.ItemId).ToList(),
GroupList = igg.First().Key.Groups.ToList()
})
)
.ToList();
https://dotnetfiddle.net/fFtqgy
Okay so the example contains 3 parties going to a "hotel" as given in your explanation.
The groups are layed out below with the times the groups will arrive and depart from the hotel
Scenario
Group 1) 15th - 20th
Group 2) 17th - 19th
Group 3) 17th - 22nd
Result Groupings
15th - 17th: Group 1
17th - 19th: Groups 1, 2 , 3
19th - 20th: Groups 1, 3
20th - 22nd: Groups 3
Explanation
This depicts the groups that will be present in the hotel for each date, a new group is created each time a group joins or leaves the hotel, which is why the code joins all of the start and end dates for all of the groups and iterates through them.
I wasn't certain what to put for the GroupId and ItemID on the resulting MasterEntity since it contains a list of items and groups, so I've set it to negative 1 in the example
Code for fiddle
public static class Utilities
{
public static bool DatesOverlap(DateTime aStart, DateTime aEnd, DateTime bStart, DateTime bEnd)
{
return aStart < bEnd && bStart < aEnd;
}
public static IList<MasterEntity> GroupFunky(IList<ItemEntity> list)
{
var result = new List<MasterEntity>();
var ordered = list.OrderBy(x => x.ItemDate).ToArray();
var startDates = list.Select(x => x.ItemDate);
var endDates = list.Select(x => x.ItemDate.AddDays(x.Duration));
var allDates = startDates.Concat(endDates).OrderBy(x => x).ToArray();
for (var index = 0; index < allDates.Length - 1; index++)
{
var group = ordered.Where(x => DatesOverlap(allDates[index], allDates[index + 1], x.ItemDate,
x.ItemDate.AddDays(x.Duration)));
var item = new ItemEntity
{
Duration = (allDates[index + 1] - allDates[index]).Days,
ItemDate = allDates[index],
Field1 = group.First().Field1,
Field2 = group.First().Field2,
Field3 = group.First().Field3,
Field4 = group.First().Field4,
GroupName = group.First().GroupName,
ItemId = -1,
GroupId = -1
};
item.ItemDate = allDates[index];
item.Duration = (allDates[index + 1] - allDates[index]).Days;
result.Add(new MasterEntity
{
Item = item,
GroupList = group.Select(x => x.GroupId).ToList(),
ItemList = group.Select(x => x.ItemId).ToList()
});
}
return result.Where(x => x.Item.Duration > 0).ToList();
}
}
public class ItemEntity
{
public int ItemId { get; set; }
public int GroupId { get; set; }
public string GroupName { get; set; }
public DateTime ItemDate { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public int Duration { get; set; }
}
public class MasterEntity
{
public ItemEntity Item { get; set; }
public List<int> ItemList { get; set; }
public List<int> GroupList { get; set; }
}
public class TestClass
{
public static void Main()
{
var items = new List<ItemEntity>
{
new ItemEntity
{
ItemId = 100,
GroupId = 1,
GroupName = "Group 1",
ItemDate = new DateTime(2018, 10, 15),
Duration = 5,
Field1 = "Item Name 1",
Field2 = "aaa",
Field3 = "bbb",
Field4 = "abc"
},
new ItemEntity
{
ItemId = 150,
GroupId = 2,
GroupName = "Group 2",
ItemDate = new DateTime(2018, 10, 17),
Duration = 2,
Field1 = "Item Name 1",
Field2 = "aaa",
Field3 = "bbb",
Field4 = "efg"
},
new ItemEntity
{
ItemId = 250,
GroupId = 3,
GroupName = "Group 3",
ItemDate = new DateTime(2018, 10, 17),
Duration = 5,
Field1 = "Item Name 1",
Field2 = "aaa",
Field3 = "bbb",
Field4 = "xyz"
}
};
var group = items.GroupBy(g => new
{
g.Field1,
g.Field2,
g.Field3
})
.Select(x => x.AsQueryable().ToList())
.ToList();
var result = group.Select(x => Utilities.GroupFunky(x));
foreach (var item in result)
{
Console.WriteLine(JsonConvert.SerializeObject(item, Formatting.Indented));
}
}
}
I havent been able to find any answers to this specific question on LINQ Group & Aggregation so am hoping someone here can help. I have a list of models of such:
public class BasketProduct
{
public ProductItem Product { get; set; }
public int Quantity { get; set; }
public decimal SubTotal { get; set; }
public DateTime DateAdded { get; set; }
}
where the first property is another model:
public class ProductItem
{
public int ID { get; set; }
public string Description { get; set; }
public char Item { get; set; }
public decimal Price { get; set; }
public string ImagePath { get; set; }
public string Barcode { get; set; }
}
I basically want to be able to Group and Aggregate on this list:
List<BasketProduct> allBasketProducts =
Using the following:
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Product.ID = y.First().Product.ID,
Product.Item = y.First().Product.Item,
Product.Description = y.First().Product.Description,
Quantity = y.Sum(z => z.Quantity),
Product.ImagePath = y.First().Product.ImagePath,
Product.Price = y.First().Product.Price,
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();
However it seriously doesn't like this (as per red squigly lines and even red'er text):
Can someone help please?
Your issue isn't actually related to LINQ, it's your ProductItem constructor. You need to construct its nested Product object explicitly, like this:
allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal),
Product = new ProductItem
{
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
}
}).ToList();
var totals =
(from b in allBasketProducts
group new { b.Quantity, b.SubTotal, Product= b.Product } by b.Product.ID into g
select new BasketProduct
{
Product = g.First().Product,
SubTotal = g.Sum(z => z.SubTotal),
Quantity = g.Sum(z => z.Quantity)
}).ToList();
Try following :
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct()
{
Product = new ProductItem() {
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
},
Quantity = y.Sum(z => z.Quantity),
SubTotal
When you specify the type of your Select, the compiler expects only the properties of that type. So you can only set the properties Product, Subtotal, Quantity and DateAdded in that code of yours.
You can find the Product simply by selecting the first Product that has an ID that matches your grouping Key:
var allBasketProductsGroupedByProductID = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct
{
Product = y.First(i => i.Product.ID == y.Key).Product,
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();
try this
List allBasketProducts = new List();
allBasketProducts = new List<BasketProduct>()
{
new BasketProduct()
{
Product = new ProductItem()
{
ID = 1,
Price = 5,
},
Quantity = 2,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 1,
Price = 5,
},
Quantity = 4,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 2,
Price = 10,
},
Quantity = 3,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 3,
Price = 20,
},
Quantity = 3,
SubTotal = 2,
},
new BasketProduct()
{
Product = new ProductItem()
{
ID = 2,
Price = 20,
},
Quantity = 3,
SubTotal = 2,
}
};
allBasketProducts = allBasketProducts
.GroupBy(x => x.Product.ID)
.Select(y => new BasketProduct()
{
Product = new ProductItem()
{
ID = y.First().Product.ID,
Item = y.First().Product.Item,
Description = y.First().Product.Description,
ImagePath = y.First().Product.ImagePath,
Price = y.First().Product.Price
},
Quantity = y.Sum(z => z.Quantity),
SubTotal = y.Sum(z => z.SubTotal)
}).ToList();
I have an object which has properties ID, brandID, brandName, NumPages, and Type.
i need to show the top 5 brands by numPage size, a brand may have multiple IDs, so I need to group by brand
listing.OrderByDescending(o => o.numPage).GroupBy(o=> o.brandName).Take(5).ToList();
is alone the lines of what im looking for but this is not valid code.
It sounds like a given brand name may have several ID's and that you want the top 5 brand's sorted by numPage. Is that correct
If so try the following
var query = listing
.GroupBy(x => x.brandName)
.OrderByDescending(brands => brands.Sum(x => x.numPage))
.Select(x => x.Key)
.Take(5);
Note: After the GroupBy operation you're now passing around a collection of the brand objects instead of single ones. Hence to order by the numPage we need to sum it for all of the brand objects in the group. The .Select(x => x.Key) will select back out the original brandName on which the group is based
just tried and it works:
public class Listing
{
public int ID { get; set; }
public int BrandID { get; set; }
public string BrandName { get; set; }
public int NumPages { get; set; }
public Type Type { get; set; }
}
Here the filtering
Listing listing1 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing2 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing3 = new Listing() { NumPages = 2, BrandName = "xx" };
Listing listing4 = new Listing() { NumPages = 3, BrandName = "xxxxx" };
List<Listing> allListings = new List<Listing>() { listing1, listing2, listing3, listing4 };
var result = allListings.OrderByDescending(x => x.NumPages).GroupBy(x => x.BrandName).Take(5);