List<T>.ForEach 带索引
Posted
技术标签:
【中文标题】List<T>.ForEach 带索引【英文标题】:List<T>.ForEach with index 【发布时间】:2012-10-24 17:14:08 【问题描述】:我正在尝试找到以下代码的 LINQ 等效项:
NameValueCollection nvc = new NameValueCollection();
List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
for(var i = 0; i < donations.Count(); i++)
// NOTE: item_number_ + i - I need to be able to do this
nvc.Add("item_number_" + i, donations[i].AccountName);
我希望我可以使用类似的东西:
NameValueCollection nvc = new NameValueCollection();
List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.ForEach(x => nvc.Add("item_name_" + ??, x.AccountName);
但我还没有找到一种方法来确定循环在哪个迭代上。任何帮助将不胜感激!
【问题讨论】:
捐款是否有 .IndexOf() 方法? 您可以使用int i = 0; donations.ForEach(x=> nvc.Add("item_name_" + i++, ...
,但不确定它有多安全。
【参考方案1】:
LINQ 没有ForEach
方法,这是有充分理由的。 LINQ 用于执行查询。它旨在从某些数据源获取信息。它不是旨在改变数据源。 LINQ 查询不应引起副作用,这正是您在这里所做的。
List
类确实有一个 ForEach
方法,这就是您正在使用的方法。因为它实际上不在 System.Linq
命名空间中,所以从技术上讲它不是 LINQ 的一部分。
您的问题中的 for
循环没有任何问题。尝试以您尝试的方式更改它是错误的(从良好实践的角度来看)。
Here 是一个更详细地讨论此事的链接。
现在,如果您想忽略该建议并使用ForEach
方法,编写一个提供操作索引的方法并不难:
public static void ForEach<T>(this IEnumerable<T> sequence, Action<int, T> action)
// argument null checking omitted
int i = 0;
foreach (T item in sequence)
action(i, item);
i++;
【讨论】:
好吧,我显然使用了错误的术语。我的Donation
集合确实有一个.ForEach()
方法。
ForEach 未定义为 IEnumerableList
的方法。这不是System.Linq
中的方法
@Servy,我认为很明显我需要离开我的电脑一段时间。我认为咖啡是正常的。谢谢你让我直截了当。
+1 以获得该文章的链接。我想知道为什么没有 ForEach 的 LINQ 实现,但这很有意义。【参考方案2】:
如果你真的想使用 List.ForEach,很简单:
//[...]
int i=0;
donations.ForEach(x => nvc.Add("item_name_" + i++, x.AccountName);
【讨论】:
【参考方案3】:这有点令人费解并创建了一个中间集合,但是怎么样:
donations.Select((x, i) => new Name = "item_name_" + i, x.AccountName)
.ToList()
.ForEach(x=> nvc.Add(x.Name, x.AccountName));
这使用the overload of Enumerable.Select
which incorporates the index。
我不得不说,这样做并没有什么真正的好处。您使用中间集合创建了更多开销,恕我直言,您失去了原始 for 循环的可读性。
如果您愿意使用foreach
循环而不是List.ForEach
,也可以跳过中间集合。请参阅@wageoghe's answer(再次强烈推荐)。
【讨论】:
【参考方案4】:这是一篇旧帖子,但在 Google 中排名很高,所以我认为更通用的方法会更合适。 (另外,我往往会忘记这是如何完成的,这意味着我每次都必须谷歌......)
假设一个整数列表:
var values = new List<int>() 2, 3, 4, 0x100, 74, 0xFFFF, 0x0F0F ;
要迭代列表并创建索引,请执行以下操作:
values.Select((x, i) => new
item = x,
index = i
)
.ToList()
.ForEach(obj =>
int value = obj.item;
int valueIndex = obj.index;
);
【讨论】:
这样做的缺点是你失去了对values
的惰性评估,因为ToList()
将在应用ForEach
之前强制枚举整个源。例如。查看两种方法的输出差异in this program。【参考方案5】:
您是否有任何理由不使用Dictionary<string, string>
,因为您的姓名/键似乎是独一无二的?这会更快,您可以使用ToDictionary
标准查询运算符。
此外,如果您确实希望使用扩展方法(尽管 Servy 说这里的 for 循环是正确的解决方案),那么您可以编写自己的方法 - 请参阅 here。
【讨论】:
【参考方案6】:捎带@lc 的答案。
foreach (var x in donations.Select((d, i) => new ItemName = "item_name_" + i, AccountName = d.AccountName))
nvc.Add(x.ItemName, x.AccountName);
【讨论】:
既然存在元组,您可以改用(d, i) => ($"item_name_i", d.AccountName)
来缩短new
的语法。然后使用x.Item1
和x.Item2
,或者命名元组字段而不是使用x
。【参考方案7】:
这也可以使用聚合来完成。检查以下示例:
var data = new[]
"A",
"B",
"C",
"D"
;
var count = data.Aggregate(0, (index, item) =>
Console.WriteLine("[0] => '1'", index, item);
return index + 1;
);
Console.WriteLine("Total items: 0", count);
Aggregate
在这里充当for
语句。唯一的缺点是每次迭代都需要将索引的值加一并返回。
【讨论】:
【参考方案8】:我喜欢这样做:
NameValueCollection nvc = new NameValueCollection();
List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", ""));
donations.Add(new BusinessLogic.Donation(0, "", "", ""));
donations.Add(new BusinessLogic.Donation(0, "", "", ""));
Enumerable
.Range(0, donations.Count())
.ToList()
.ForEach(i => nvc.Add("item_number_" + i, donations[i].AccountName));
【讨论】:
【参考方案9】:一个糟糕的解决方案是简单地使用 select 并返回一个无用的值。
items.toList().Select((el, index) =>
el.doStuff();
return 0;
);
【讨论】:
我刚做了这个。不要告诉任何人,哈哈。 @JasonC 你的秘密对我来说是安全的 这段代码的问题几乎和单词一样多。它什么也不做,因为该语句没有被执行。并且当拼写被纠正时,ToList
是多余的。 Select
主体对 index
没有任何作用。执行时,它只会返回一个零列表并(可能)更改源对象,这不会被询问。它不会向NameValueCollection nvc
添加任何内容。糟糕,当然。
@GertArnold 用它的伪代码来传达这个概念。不是可粘贴的样品。如果你要抱怨拼写,你应该知道句子不以“And”开头。 ToList
不是多余的,因为它暗示 items 是需要转换的标准类型数组。它不需要对索引做任何事情,它只是表明它在你需要时可用。 foreach
首先不需要返回任何东西,所以我不知道你为什么抱怨这个。
伪代码在答案中并不是很有用。此外,已经有足够多的答案表明Select
的过载。并告诉如何在实际代码中使用它。【参考方案10】:
C# 7(大约 2017 年)添加了更简洁的元组语法,可以与 Select
的索引形式一起使用(无需手动计算索引),以提供简洁明了的语法。例如:
foreach ((var item, int n) in TheItems.Select((i,n)=>(i,n)))
// item is the current item, n is its index
或者,如果您愿意:
foreach (var element in TheItems.Select((item,n)=>(item,n)))
// element.item is the current item, element.n is its index
所以在 OP 的情况下,例如:
foreach ((var item, int n) in donations.Select((i,n)=>(i,n)))
nvc.Add($"item_numbern", item.AccountName);
或者,如果您愿意:
foreach ((var k, var v) in donations.Select((i,n)=>($"item_number_n",i.AccountName)))
nvc.Add(k, v);
或者,类似于this answer,但略显冗长,但请注意,这将通过调用ToList()
杀死惰性求值,这会在调用ForEach()
之前强制枚举整个源:
TheItems.Select((item,n)=>(item,n)).ToList().ForEach(element =>
// element.item is the current item, element.n is its index
);
【讨论】:
我觉得这个解决方案应该已经在这里了——嗯,基于这个Select
过载有几个答案。这里有什么新东西?
@GertArnold foreach over Select,不维护索引计数器,代码更简洁。大多数“基于”选择的答案都会做一些额外的事情或扼杀懒惰的评估。
这里唯一重要的是Select
重载。你让它看起来你是第一个提到它的人。即便如此,真的没有必要添加无数种如何使用它。与此同时,人们可能想知道为什么没有人直接使用Select
和ToList
而没有Foreach
或foreach
直接从donations
创建nvc
。那将是最不冗长的解决方案。不过,我不觉得有任何添加另一个答案的冲动。
@GertArnold 在一般意义上(尽管在 OP 的情况下不是问题),使用 ToList()
将在继续之前强制枚举整个源系列。因此,如果源是一些惰性生成的 IEnumerable,您将失去那里的惰性求值,这在某些情况下可能是一个问题。
@GertArnold Re:直接创建nvc
,an.dreas.k's answer 非常接近,但实际上没有任何方法可以生成这样的NameValueCollection
。 ToDictionary()
无济于事(但devdigital 的建议将使其成为可能),虽然您可以使用例如构建 NVC。 Aggregate
之类的,此时您不妨使用Add
。此外,尚不清楚 OP 是否打算创建新的 NVC 或添加到现有的 NVC,但从问题来看,它看起来更像后者。【参考方案11】:
试试这个 -
donations.ForEach(x =>
int index = donations.IndexOf(x);
nvc.Add("item_name_" + index, x.AccountName);
);
【讨论】:
最好用索引枚举一个中间集合(@lc 的答案),而不是在每次迭代时搜索列表 (.IndexOf
)
如果一个项目多次出现在列表中会怎样? (请注意,在 OP 的代码中,所有项目都是相同的。)毕竟,这不仅仅是关于糟糕的性能。它甚至不工作。
那么,有没有其他方法可以在不创建中间集合的情况下找到索引..?
@Sevy - 哎呀..!!是的,现在对我来说很有意义。完全忘记唯一性部分。 Downvotes 现在光荣地接受了。 :)
@Sevy - 是的,谢谢Servy 现在明白了。这就是为什么我说至少告诉我哪里错了。以上是关于List<T>.ForEach 带索引的主要内容,如果未能解决你的问题,请参考以下文章