从列表中选择值
本文关键字:选择 列表 | 更新日期: 2023-09-27 18:17:34
我有以下列表:
public class Products
{
public string SKU;
public int WarehouseID;
}
List<Products> products = new List<Products>();
,在填充列表后,我得到以下数据:
ProductCode|WarehouseID
SKU001|2
SKU001|3
SKU002|3
SKU003|3
SKU004|1
SKU004|5
我有多个SKU,因为该项目可以从多个仓库位置供应,因为它有库存。在SKU001的情况下,仓库ID 2比仓库ID 3有更多的库存。
我需要从最少数量的仓库位置中选择物品。我想以
结尾SKU001|3
SKU002|3
SKU003|3
SKU004|1
这限制了产品选择只有2个位置:SKU001, SKU002 &SKU003都可以从仓库ID 3中获得。理想情况下,从库存最多的地点选择,但限制地点的数量更重要。
我正在使用Linq来尝试和实现这一点,同时尝试循环每个列表项,但我挣扎,因为Linq不是我的强项。我尝试首先使用
获得重复仓库id的最高计数products.GroupBy(i => i).OrderByDescending(grp => grp.Count()).Select(grp => grp.Key).FirstOrDefault();
但是我在其他项目上迷路了。有什么好主意吗?
您可以在多个语句中执行此操作,首先获取WarehouseID
以及每个仓库中的不同产品及其计数,如:
var query = products.GroupBy(r => r.WarehouseID)
.Select(grp => new
{
WarehouseID = grp.Key,
DistinctProducts = grp.Select(r => r.SKU).Distinct(),
DistinctCount = grp.Select(r => r.SKU).Distinct().Count(),
});
之后创建如下的结果列表:
List<Products> result = new List<Products>();
foreach (var item in query.OrderByDescending(r => r.DistinctCount)) //warehouse with most products
{
if (!result.Any(r => item.DistinctProducts.Any(t => t == r.SKU)))
{
result.AddRange(item.DistinctProducts.Select(r => new Products { SKU = r, WarehouseID = item.WarehouseID }));
}
}
输出:foreach (var item in result)
{
Console.WriteLine("{0} | {1}", item.SKU, item.WarehouseID);
}
输出:SKU001 | 3
SKU002 | 3
SKU003 | 3
SKU004 | 1
下面是生成最小仓库id集的解决方案:
// Number of product SKUs
int nProducts = products.Select(p => p.SKU).Distinct().Count();
// Warehouses and their products
Dictionary<int, List<Product>> warehouses = products
.GroupBy(p => p.WarehouseID)
.ToDictionary(g => g.Key, g => g.ToList());
List<int> minWarehouseSet = warehouses
// Get list of unique warehouse ids
.Select(p => p.Key).ToList()
// Get all combinations of warehouses
.Combinations()
// Order sets by the number of items
.OrderBy(ws => ws.Count())
// Find set which satisfies requirement (contains all products)
.First(ws => ws.SelectMany(w => warehouses[w]).Distinct().Count() == nProducts)
.ToList();
foreach (var product in products.Where(p => minWarehouseSet.Contains(p.WarehouseID)))
Console.WriteLine("{0}|{1}", product.SKU, product.WarehouseID);
需要以下扩展方法:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> allValues)
{
for (int counter = 0; counter < (1 << allValues.Count); ++counter)
yield return allValues.Where((_, i) => (counter & 1 << i) == 0).ToList();
}
输出:SKU001|3
SKU002|3
SKU003|3
SKU004|5
指出:
- 此解决方案执行最优仓库集的暴力搜索。也许有更好的解决办法。
- 仓库的数量限制为
Cominations
方法中int
的位数(32)。您可以选择不同的变量类型或以不同的方式实现该方法。 - 你们提供的数据不包括库存,你们没有说明它是如何影响结果的。如果您需要考虑库存,请调整排序(用于测量结果的"质量")或过滤(用于严格条件)。
Edit:我已经更新了我的查询,以根据每个仓库中库存的产品数量进行选择,而不是根据每个产品的库存。
var productQ2 = products.GroupBy(product => product.WarehouseId)
.SelectMany(wGroup => wGroup.Select(product => new
{
Product = product,
WarehouseProductCount = wGroup.Select(p => p.SKU)
.Distinct()
.Count()
}))
.GroupBy(product => product.Product.SKU)
.Select(pGroup => pGroup.OrderByDescending(product => product.WarehouseProductCount).First().Product);
原始答:
无法访问每个仓库的库存数量,因此需要将其添加到模型中。
public class Product
{
public string SKU;
public int WarehouseID;
public int StockCount;
}
List<Product> products = new List<Product>();
然后得到你的查询结果:
products.GroupBy(product => product.SKU)
.Select(group => group.OrderByDescending(product => product.StockCount).First())