有没有更好的方法来做到这一点,也许用 LINQ ish的东西替换 for/foreach 循环
本文关键字:替换 ish 循环 foreach LINQ for 也许 方法 更好 有没有 这一点 | 更新日期: 2023-09-27 18:32:34
我有一个现有资产列表,我想合并到另一个资产列表中。 我知道使用 foreach 和 for 循环是一种糟糕的方法,但我想不出使用 LINQ 来减少这种情况的好方法。
private void CombineHoldings(List<Holding> holdingsToAdd, ref List<Holding> existingHoldings)
{
foreach (Holding holdingToAdd in holdingsToAdd)
{
Boolean found = false;
for (int i = 0; i < existingHoldings.Count; i++)
{
if (existingHoldings[i].Sector == holdingToAdd.Sector)
{
found = true;
existingHoldings[i].Percentage += holdingToAdd.Percentage;
}
}
if (!found)
existingHoldings.Add(holdingToAdd);
}
foreach (Holding holding in existingHoldings)
holding.Fund = "Combined Funds";
}
让函数改变原始列表会使它非常非 Linq,因此这里有一个将两个列表视为不可变的版本:
private List<Holding> CombineHoldings(
List<Holding> holdingsToAdd,
List<Holding> existingHoldings)
{
var holdings = existingHoldings.Concat(holdingsToAdd)
.GroupBy(h => h.Sector)
.Select(g =>
{
var result = g.First();
result.Percentage = g.Select(h => h.Percentage).Sum();
return result;
});
return holdings.ToList();
}
绝对不会赢得性能比赛,但我喜欢它的简单性。以下内容可能会更快,但更复杂,并且需要您覆盖持股的相等性以比较行业或创建IEqualityComparer<Holding>
:
private List<Holding> CombineHoldings(
List<Holding> holdingsToAdd,
List<Holding> existingHoldings)
{
var holdings = existingHoldings.GroupJoin(holdingsToAdd, h => h, h => h,
(h, toAdd) =>
new Holding(
h.Sector,
/*Other parameters to clone*/,
h.Percentage + toAdd.Select(i => i.Percentage).Sum())
).ToList();
holdings.AddRange(holdingsToAdd.Except(holdings));
return holdings;
};
如果您经常在列表中调用此方法,那么我建议将其放入列表类型的扩展方法中,即
private static void CombineHoldings(this List<Holding> holdingsToAdd, ref List<Holding> existingHoldings)
{
foreach (Holding holdingToAdd in holdingsToAdd)
{
Boolean found = false;
for (int i = 0; i < existingHoldings.Count; i++)
{
if (existingHoldings[i].Sector == holdingToAdd.Sector)
{
found = true;
existingHoldings[i].Percentage += holdingToAdd.Percentage;
}
}
if (!found)
existingHoldings.Add(holdingToAdd);
}
foreach (Holding holding in existingHoldings)
holding.Fund = "Combined Funds";
}
这将允许您在创建列表的任何地方
List<Holding> temp1 = new List<Holding>();
List<Holding> temp2 = new List<Holding>();
//add here to temp1 and temp2
//then...
temp1.CombineHoldings(temp2);
将第一个方法设为静态并将"this"关键字放在第一个参数前面意味着它将扩展该类型
查看参数,尽管切换两者可能更有意义,以便将其添加到调用该方法的列表中
,如下所示 -private static void CombineHoldings(this List<Holding> existingHoldings, List<Holding> holdingsToAdd)
我可能会选择这样的东西:
private void CombineHoldings(List<Holding> holdingsToAdd, ref List<Holding> existingHoldings)
{
// group the new holdings by sector
var groupedHoldings = holdingsToAdd.GroupBy(h => h.Sector);
// now iterate over the groupings
foreach(var group in groupedHoldings) {
// calculate the sum of the percentages in the group
// we'll need this later
var sum = group.Sum(h => h.Percentage);
// get the index of a matching object in existing holdings
var existingHoldingIndex = existingHoldings.FindIndex(h => h.Sector == group.Key);
// yay! found one. add the sum of the group and our job's done.
if(existingHoldingIndex >= 0) {
existingHoldings[existingHoldingIndex].Percentage += sum;
continue;
}
// didn't find one, so take the first holding in the group, set its percentage to the sum
// and append that to the existing holdings table
var newHolding = group[0];
newHolding.Percentage = sum;
existingHoldings.Add(newHolding);
}
}
在性能方面,我不确定这如何维持。但它似乎更优雅一些。
你的问题有点模棱两可,你想摆脱foreach循环是因为for循环更快,因为你觉得你有一个太多的循环,还是因为你想要更好的性能?
假设这是一个关于提高性能的问题,我建议将现有控股从列表更改为排序列表,其中T是Holding.Sector的类型。 为了获得最佳性能,扇区应该是整数变量类型,如 int。
private void CombineHoldings(List<Holding> holdingsToAdd, SortedList<int,Holding> existingHoldings) //Remove ref since List and SortedList are reference types and we are not changing the pointer.
{
for (int i = 0; i < holdingsToAdd.Count; i++)
{
if (existingHoldings.ContainsKey(holdingsToAdd[i].Sector))
{
existingHoldings[holdingsToAdd[i].Sector].Percentage += holdingsToAdd[i].Percentage;
}
else
{
existingHoldings.Add(holdingsToAdd[i].Sector, holdingsToAdd[i]);
}
}
for (int i = 0; i < existingHoldings.Count; i++)
{
existingHoldings.Values[i].Fund = "Combined Funds";
}
}
此方法将产生 O(m*log n + n(,其中 n 是现有控股中的元素数,m 是 holdingsToAdd 中的元素数。 不幸的是,所有现有的控股元素都必须更新其基金价值,因为它增加了通过该集合的额外传递。
注意:如果您不断从现有馆藏中添加/删除项目,那么您可以使用 SortedDictionary,它应该更快(SortedList 访问元素的速度更快,但添加/删除需要更长的时间(
编辑:请务必注意,LINQ 用于搜索集合,而不是更新集合。 因此,您可以使用 LINQ 查找现有持股中存在和不存在的持股,然后循环遍历现有持股设置基金,并在需要时设置百分比,但随后需要对持股 ToAdd 和现有持股进行排序,并且您仍将循环遍历每个集合一次。 它将是O(2*m*log n + n(的数量级。 编译器可能能够将两个查询合并到一个调用中,但即便如此,您也会看到类似的性能,但可读性较低。
也许这可能很有用。来自 MSDN 的链接。
如何:填充来自多个源的对象集合 (LINQ(
从另一个问题中找到此链接 https://stackoverflow.com/a/9746336/1278872