web-dev-qa-db-ja.com

List <T> .ForEach with index

次のコードに相当する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);

しかし、ループがどのイテレーションにあるかを判別する方法が見つかりませんでした。どんな助けもいただければ幸いです!

16
James Hill

LINQにはForEachメソッドがありません。これには正当な理由があります。 LINQはクエリを実行するためのものです。一部のデータソースから情報を取得するように設計されています。データソースを変更するように設計されているのではありません。 LINQクエリによって副作用が発生することはありません。これがまさにここで行っていることです。

Listクラスdoesには、使用しているForEachメソッドがあります。実際にはSystem.Linq名前空間は技術的にはLINQの一部ではありません。

質問のforループに問題はありません。あなたがしようとしている方法でそれを変更しようとすることは(良い習慣の観点から)間違っているでしょう。

ここ は、問題をより詳細に説明するリンクです。

さて、そのアドバイスを無視して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++;
    }
}
23
Servy

あなたが本当にList.ForEachを使いたいなら、それは簡単です:

//[...]
int i=0;
donations.ForEach(x => nvc.Add("item_name_" + i++, x.AccountName);
11
Tiago Raposo

それは少し複雑で中間的なコレクションを作成しますが、どうですか:

donations.Select((x, i) => new {Name = "item_name_" + i, x.AccountName})
    .ToList()
    .ForEach(x=> nvc.Add(x.Name, x.AccountName));

これは インデックスを組み込んだEnumerable.Selectのオーバーロード を使用します。

私はこの方法でそれを行うことから本当に得るものは何もないと主張する必要があります。中間コレクションを使用するとオーバーヘッドが増え、IMHOは元のforループよりも読みにくくなります。

List.ForEachの代わりにforeachループを使用する場合は、中間コレクションをスキップすることもできます。 @ wageoghe's answer を参照してください(これも強くお勧めします)。

10
lc.

名前/キーが一意であるように見えるため、Dictionary<string, string>を使用しない理由はありますか?これはより高速で、ToDictionary標準クエリ演算子を使用できます。

また、拡張メソッドを使用したい場合(Servyはforループがここでの適切なソリューションであると言いますが)、独自に記述することもできます ここ を参照してください。

3
devdigital

これは古い投稿ですが、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;
});
3
Patrick Ribbing

@lcによる回答の便乗。

foreach (var x in donations.Select((d, i) => new {ItemName = "item_name_" + i, AccountName = d.AccountName}))
{
  nvc.Add(x.ItemName, x.AccountName);
}
3
wageoghe

私はそれを次のようにしたいです:

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));
0
an.dr.eas.k

これは、集約を使用して行うこともできます。次の例を確認してください。

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ステートメントとして機能します。唯一の欠点は、反復ごとにインデックスの値を1ずつ増やして返す必要があることです。

0