This question already has answers here:
LINQ with groupby and count
(3 answers)
List array duplicates with count
(7 answers)
Closed 1 year ago.
I need to iterate over a list and count all the elements with the same id given this model:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
I want to check for all the repeated elements in the list and store the count in a variable:
List<Product> productList = new()
{
new Product { ProductName = "item1", ProductId = 1},
new Product { ProductName = "item2", ProductId = 1},
new Product { ProductName = "item3", ProductId = 2},
new Product { ProductName = "item4", ProductId = 2},
new Product { ProductName = "item5", ProductId = 3},
new Product { ProductName = "item6", ProductId = 1}
};
Given the example above, the result should look like this:
int someProduct1 = 3;
int someProduct2 = 2;
int someProduct3 = 1;
How can I achieve something like that?
try this
var query = productList
.GroupBy(c => c.ProductId)
.Select(o => new
{
ProductId = o.Key,
Count = o.Count()
});
or just repeatings
var query = productList
.GroupBy(c => c.ProductId)
.Select(o => new
{
ProductId = o.Key,
Count = o.Count()
})
.OrderByDescending (o => o.Count )
.Where(p => p.Count>1);
output
productId Count
1 3
2 2
Related
I have a class "item":
public class Item
{
public int Id { get; set; }
public decimal Price { get; set; }
public override bool Equals(object obj)
{
if (obj is Item item)
return item.Id == Id;
return false;
}
// GetHashCode omitted...
}
And I have 2 lists that I need to union:
var items1 = new List<Item>
{
new Item { Id = 1, Price = 10 },
new Item { Id = 2, Price = 10 },
new Item { Id = 3, Price = 10 },
};
var items2 = new List<Item>
{
new Item { Id = 1, Price = 10 },
new Item { Id = 2, Price = 8 },
new Item { Id = 4, Price = 10 },
};
The union I get like this:
var union = items1.Union(items2).ToList();
But I need also the constraint that the items with the lowest price is in the union. So for example in the above lists Item.ID = 2 from "items2" must be in the union...so the result should be a list consisting of these 4 items:
Item { Id = 1, Price = 10 }
Item { Id = 2, Price = 8 } // Not the one with Price = 10
Item { Id = 3, Price = 10 }
Item { Id = 4, Price = 10 }
Is there an elegant way of doing this in C# (preferably using Linq)?
You can try using groupby, like below :
var result = items1.Union(items2).GroupBy(x => x.Id)
.Select(x => new Item
{
Id = x.Key,
Price = x.Min(i => i.Price)
});
i have 2 tables in a database
supplier table: SupplierID - SupplierName
product table: ProductID - ProductName - UnitsInStock - SupplierID
how can i select the supplier that has largest UnitsInStock?
here's the code i have
private storeDBEntities2 db1 = new storeDBEntities2();
public ActionResult Index()
{
var product = db1.Products.Where(e => e.UnitsInStock == 0);
var largestSupplier = db1.Products.GroupBy(e => e.SupplierID);
Product minimord = db1.Products.OrderBy(e => e.UnitsOnOrder).FirstOrDefault();
var supplier = // this is the query i am struggling with
AllModelsProduct all = new AllModelsProduct { Iproduct = product.ToList(), product = new Product(),largestSupplierOfTheStore = supplier,minimumOrders = minimord };
return View(all);
}
here's a picture of my data
i need to get supplierID 345 as we have 20 units belong to him in store which is greater than the other supplier with 5 + 3 + 0 = 8 units
If all you're looking to do is find the supplier with the largest number of UnitsInStock then this should do the trick.
I have created a dotNetFiddle for you to observe.
But here it is anyway:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Supply> lstSuppliers = new List<Supply>();
Supply supply1 = new Supply() { ID = 1, SupplierName = "Supplier One"};
Supply supply2 = new Supply() { ID = 2, SupplierName = "Supplier Two"};
lstSuppliers.Add(supply1);
lstSuppliers.Add(supply2);
Product product1 = new Product() {ID = 1, UnitsInStock = 3, SupplierID = 1};
Product product2 = new Product() {ID = 2, UnitsInStock = 3, SupplierID = 2};
Product product3 = new Product() {ID = 3, UnitsInStock = 5, SupplierID = 1};
List<Product> lstAllProducts = new List<Product>();
lstAllProducts.Add(product1);
lstAllProducts.Add(product2);
lstAllProducts.Add(product3);
var findSupplierId = lstAllProducts.GroupBy(x => x.SupplierID).Select(x => new{ Supplier = x.Key.ToString(), Count = x.Sum(g => g.UnitsInStock)}).OrderByDescending(x => x.Count).First().Supplier;
Console.WriteLine(findSupplierId);
Console.WriteLine(lstSuppliers.Single(x => x.ID.ToString() == findSupplierId).SupplierName);
}
}
public class Supply{
public int ID {get;set;}
public string SupplierName {get;set;}
}
public class Product{
public int ID {get;set;}
public int UnitsInStock {get;set;}
public int SupplierID {get;set;}
}
This uses the GroupBy, along with creating anonymous classes to get the desired outcome.
Let me know if this helps!
Update - To show if multiple suppliers have the same units in stock
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Supply> lstSuppliers = new List<Supply>();
Supply supply1 = new Supply() { ID = 1, SupplierName = "Supplier One"};
Supply supply2 = new Supply() { ID = 2, SupplierName = "Supplier Two"};
Supply supply3 = new Supply() { ID = 3, SupplierName = "Supplier Three"};
lstSuppliers.Add(supply1);
lstSuppliers.Add(supply2);
lstSuppliers.Add(supply3);
Product product1 = new Product() {ID = 1, UnitsInStock = 3, SupplierID = 1};
Product product2 = new Product() {ID = 2, UnitsInStock = 3, SupplierID = 2};
Product product3 = new Product() {ID = 3, UnitsInStock = 5, SupplierID = 1};
Product product4 = new Product() {ID = 4, UnitsInStock = 8, SupplierID = 3};
List<Product> lstAllProducts = new List<Product>();
lstAllProducts.Add(product1);
lstAllProducts.Add(product2);
lstAllProducts.Add(product3);
lstAllProducts.Add(product4);
// finds largest supplier
//var findSupplierId = lstAllProducts.GroupBy(x => x.SupplierID).Select(x => new{ Supplier = x.Key.ToString(), Count = x.Sum(g => g.UnitsInStock)}).OrderByDescending(x => x.Count).First().Supplier;
//Console.WriteLine(lstSuppliers.Single(x => x.ID.ToString() == findSupplierId).SupplierName);
// What if there are multiple suppliers with the same number of units in stock?
// first - we have to find the largest number of units in stock
var findLargestNumberUIS = lstAllProducts.GroupBy(x => x.SupplierID).Select(x => new{ Supplier = x.Key.ToString(), Count = x.Sum(g => g.UnitsInStock)}).Max(x => x.Count); // 8
// second - gather a list of suppliers where their units in stock == findLargestNumberUIS
var lstOfLargestSuppliers = lstAllProducts.GroupBy(x => x.SupplierID).Select(x => new{ Supplier = x.Key.ToString(), Count = x.Sum(g => g.UnitsInStock)}).Where(x => x.Count == findLargestNumberUIS).ToList();
// third - loop through lstOfLargestSuppliers to get all suppliers that have the same amount of units in stock which happen to be the largest
foreach(var item in lstOfLargestSuppliers){
var supplier = lstSuppliers.Single(x => x.ID.ToString() == item.Supplier).SupplierName;
Console.WriteLine(supplier); // print the supplier names to console
// Output - Supplier One
// Supplier Three
}
}
}
public class Supply{
public int ID {get;set;}
public string SupplierName {get;set;}
}
public class Product{
public int ID {get;set;}
public int UnitsInStock {get;set;}
public int SupplierID {get;set;}
}
I have a question about a LINQ grouping.
I thought that grouping would be a simple matter of using the GroupBy function on the result set and specifying what to group it by. However my items appear to not be grouping together and instead are displaying as if the GroupBy function wasn't there. I want to group by the itemPk, but I'm can't seem to do it. I have tried grouping by both category.ItemFk and Item.Itempk, but no luck. Could someone give me a pointer on this?
var itemIds = items.Select(i => i.ItemId).ToList();
var itemAndCatJoin =
from item in Context.SCS_Items
join category in Context.SCS_ItemCategories
on item.ItemPk equals category.ItemFk
into temp
from category in temp.DefaultIfEmpty()
select new ExportItemTable
{
Category = category,
Item = item
};
return itemAndCatJoin.Where(i => itemIds.Contains(i.Item.ItemPk))
.GroupBy(n => new {n.Item, n.Category})
.Select(i => new ExportableItem
{
ItemPk = i.Key.Item.ItemPk,
Name = i.Key.Item.Name,
Description = i.Key.Item.Description,
Price = i.Key.Item.Price,
Category = i.Key.Category.Category.Category_Name,
GLDepartment = i.Key.Category.GL_Department.Name ?? "",
GLName = i.Key.Category.GL_Name.Name ?? "",
StartDate = i.Key.Item.StartDate,
EndDate = i.Key.Item.EndDate,
FiscalYear = i.Key.Item.SCS_FiscalYear.Name,
School = i.Key.Item.School != null ? i.Key.Item.School.School_Name : i.Key.Item.Board.Board_Name,
Beneficiary = i.Key.Item.SCS_Beneficiary.Name,
Quantity = i.Key.Item.MaxQuantity,
Deleted = i.Key.Item.DeletedFlag,
OptionalStudents = i.Key.Item.SCS_Attachments.Where(a => !a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
RequiredStudents = i.Key.Item.SCS_Attachments.Where(a => a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
IsPublic = i.Key.Item.IsPublic,
AllowRecurring = i.Key.Item.AllowRecurringPayments,
EffectiveCutoff = i.Key.Item.SCS_Attachments.Where(a => !a.DeletedFlag && a.CourseDropCutoff.HasValue).Select(a => a.CourseDropCutoff).OrderBy(a => a).FirstOrDefault(),
CreatedDate = i.Key.Item.CreatedDate
}).OrderBy(i => i.ItemPk).ToList();
}
your groupbyy is indeed doing nothing for you, you need to tell the groupby what to group by....
like
.GroupBy(n => n.Category)
Here is a simple example to your grouping question:
class Program
{
static void Main()
{
var allItems = GetAllItems();
var groups = from item in allItems
group item by item.Category
into newGroup
select newGroup;
foreach (var group in groups)
{
Console.WriteLine($"\nCategory: {group.Key}");
foreach (var item in group)
{
Console.WriteLine($"{item.Name}: {item.Price}");
}
}
Console.ReadLine();
}
static List<Category> GetAllCategories()
{
return new List<Category>()
{
new Category() { Id = 1, Name = "Programming Books" },
new Category() { Id = 2, Name = "Fiction Books" }
};
}
static List<Item> GetAllItems()
{
return new List<Item>()
{
new Item() { Id = 1, Name = "Embedded Linux", Category = 1, Price = 9.9 },
new Item() { Id = 2, Name = "LINQ In Action", Category = 1, Price = 36.19 },
new Item() { Id = 3, Name = "C# 6.0 and the .NET 4.6 Framework", Category = 1, Price = 40.99 },
new Item() { Id = 4, Name = "Thinking in LINQ", Category = 1, Price = 36.99 },
new Item() { Id = 5, Name = "The Book Thief", Category = 2, Price = 7.99 },
new Item() { Id = 6, Name = "All the Light We Cannot See", Category = 2, Price = 16.99 },
new Item() { Id = 7, Name = "The Life We Bury", Category = 2, Price = 8.96 }
};
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
This example is simple enough for anyone new to LINQ. I am sure you can make some adjustment to make it work for your specific issue. Hope this will help.
I have list that includes class named 'ID', 'Name' and 'Category'. There are 6 item in list.
List<MyData> list =
{
{0, "John", "Police"},
{1,"Michael", "Police"},
{2,"Alice", "Police"},
{3, "Ferdinand", "Thief"},
{4, "Jocas", "Thief"},
{5, "Connor", "Thief"}
};
I wanna list them with limited quantity per group by 'Category' with LINQ.
Example : I want list 2 item for each 'Cateogory'. Listed should be below :
John Police
Michael Police
Ferdinand Thief
Jocas Thief
Use combination of Take and SelectMany:
var results = list.GroupBy(x => x.Category).SelectMany(g => g.Take(2)).ToList();
I've tested it on following Item class:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Category { get; set; }
}
And query:
List<Item> list = new List<Item>
{
new Item { ID = 0, Name = "John", Category = "Police"},
new Item { ID = 1, Name = "Michael", Category = "Police"},
new Item { ID = 2, Name = "Alice", Category = "Police"},
new Item { ID = 3, Name = "Ferdinand", Category = "Thief"},
new Item { ID = 4, Name = "Jocas", Category = "Thief"},
new Item { ID = 5, Name = "Connor", Category = "Thief"}
};
var results = list.GroupBy(x => x.Category).SelectMany(g => g.Take(2)).ToList();
Returns 4 elements, right as you want.
Oh, I just find that the error is caused by another part of code.
Case closed.
I have 2 tables
1- userinfo
id uid name
1 11 Billy
2 22 Paul
3 33 Joshua
2- Score
id uid score
1 11 30
2 22 40
3 11 50
4 11 60
5 33 20
6 33 70
7 33 80
I have a class called ScoreUser
public class ScoreUser{
public long uid{get; set;}
public string name{get;set;}
public int score{get;set;}
}
I want to use linq to query the above two tables, get the maximum score of each user and map it into the ScoreUser Object.
I use the following code:
from s in Scores
join i in UserInfos
on s.uid equals i.uid
group uscore by new { s.uid, i.name} into g
let maxScore = g.Max(p => p.score)
select new ScoreUser
{
uid = g.Key.uid,
name = g.Key.name,
score = maxScore
}
However, this code does does not work. It produces 7 objects instead of 3.
What should I do?
You are also grouping by score when it should be the aggregator. Try this:
from s in Scores
join i in UserInfos on s.uid equals i.uid
group by new { s.uid, i.name } into g
select new ScoreUser
{
uid = g.Key.uid
name = g.Key.name,
score = g.Max(p => p.score)
}
(update)
I see you found the problem. However I leave you here a test to this query:
class UserInfo
{
public int Id { get; set; }
public int UId { get; set; }
public string Name { get; set; }
}
class Score
{
public int Id { get; set; }
public int UId { get; set; }
public int SScore { get; set; }
}
public class ScoreUser
{
public int uid { get; set; }
public string name { get; set; }
public int score { get; set; }
public override string ToString()
{
return string.Format("UId:{0} Name:{1} Score:{2}", uid, name, score);
}
}
static void Main(string[] args)
{
List<UserInfo> infos = new List<UserInfo>()
{
new UserInfo {Id = 1, UId = 11, Name = "Billy"},
new UserInfo {Id = 2, UId = 22, Name = "Paul"},
new UserInfo {Id = 3, UId = 33, Name = "Joshua"}
};
List<Score> scores = new List<Score>()
{
new Score {Id = 1, UId = 11, SScore = 30},
new Score {Id = 2, UId = 22, SScore = 40},
new Score {Id = 3, UId = 11, SScore = 50},
new Score {Id = 4, UId = 11, SScore = 60},
new Score {Id = 5, UId = 33, SScore = 20},
new Score {Id = 6, UId = 33, SScore = 70},
new Score {Id = 7, UId = 33, SScore = 80}
};
var qry = from s in scores
join i in infos on s.UId equals i.UId
group s by new { s.UId, i.Name } into g
select new ScoreUser
{
uid = g.Key.UId,
name = g.Key.Name,
score = g.Max(p => p.SScore)
};
foreach (var su in qry)
{
Console.WriteLine(su);
}
}
Prints:
UId:11 Name:Billy Score:60
UId:22 Name:Paul Score:40
UId:33 Name:Joshua Score:80
for someone's flavor, providing here two LINQ using lambda expressions to select maximum value in a group
LINQ using lambda expressions
qry = Scores.Join(UserInfos, s => s.uid, i => i.uid, (s, i) => new { s, i })
.GroupBy(g => new { g.s.uid, g.i.name })
.Select(g => new ScoreUser
{
uid = g.Key.uid,
name = g.Key.name,
score = g.Max(p => p.s.score)
});
LINQ(Lambda) using LastOrDefault() to get Max() value, so the class ScoreUser() can be eliminated here.
var qry = Scores.Join(UserInfos, s => s.uid, i => i.uid, (s, i) => new { s, i })
.GroupBy(g => new { g.s.uid, g.i.name })
.Select(g => new { su = g.OrderBy(i => i.s.score) })
.Select(x => x.su.LastOrDefault()).ToList();
Both get same results as LINQ with query syntax.
This document may be interested
LINQ Query Syntax versus Method Syntax.
var list = records.GroupBy(p => p.Year, (key, g) => g.OrderByDescending(y => y.Month).First()).ToList();
Above query will return a list that includes the highest month item by grouping the years.