LINQを使用してコレクション内のすべてのオブジェクトを更新する


500

LINQを使用して以下を実行する方法はありますか?

foreach (var c in collection)
{
    c.PropertyToSet = value;
}

明確にするために、コレクション内の各オブジェクトを反復処理してから、各オブジェクトのプロパティを更新したいと思います。

私のユースケースは、ブログ投稿に大量のコメントがあり、ブログ投稿の各コメントを反復処理し、ブログ投稿の日時を+10時間に設定することです。私はSQLでそれを行うことができましたが、ビジネス層でそれを保ちたいです。


14
興味深い質問です。個人的に私はあなたがそれを上に持っている方法を好みます-何が起こっているのかはるかに明確です!
noelicus

8
私は同じ質問への答えを探してここに来て、同じように簡単で、コードが少なく、OPで行った方法でそれを行うことが将来の開発者にとって理解しやすいと判断しました。
Casey Crookston、2017

4
なぜそれをLINQで行いたいのですか?
Caltor 2017

13
この質問は間違ったことを尋ねます。唯一の正しい答えは、データソースを変更するためにLINQを使用しないことです
Tim Schmelter

この質問に対する回答のほとんどすべてが、新しいプログラマーによるLINQの理解に積極的に有害であるため、この質問をトピック外として閉じることにします。
Tanveer Badar

回答:


842

あなたが使用することができますがForEach、あなたが行うことができますだけでフレームワークを使用する場合は、拡張メソッドを

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();

ToList起因して、すぐに選択し評価するために必要とされる遅延評価


6
これはかなりいい解決策なのでこれに賛成しました。拡張メソッドが好きな唯一の理由は、何が起こっているのかを正確に理解することが少し明確になることです...しかし、あなたの解決策はまだかなり甘いです
lomaxx

9
コレクションがObservableCollection発言である場合は、新しいリストを作成するのではなく、適切な場所でアイテムを変更すると便利です。
Cameron MacFarland、2010年

7
@desaivvええ、これは少し構文の乱用なので、Resharperはこれについて警告しています。
Cameron MacFarland 2012

46
私見、これは単純なforeachループよりはるかに表現力に欠けます。ToList()は、それ以外の場合は延期されるであろう強制的な評価以外には何も使用されないため、混乱を招きます。プロジェクションは、意図された目的に使用されていないため、混乱も招きます。むしろ、コレクションの要素を反復処理し、更新できるようにプロパティへのアクセスを許可するために使用されます。私の頭の中の唯一の質問は、foreachループがParallel.ForEachを使用して並列処理から利益を得ることができるかどうかですが、それは別の質問です。
フィリップ

37
この答えは最悪の習慣です。絶対にしないでください。
Eric Lippert

351
collection.ToList().ForEach(c => c.PropertyToSet = value);

36
@SanthoshKumar:使用collection.ToList().ForEach(c => { c.Property1ToSet = value1; c.Property2ToSet = value2; });
ΕГИІИО

@CameronMacFarland:もちろん、構造体は不変なので、そうなりません。しかし、あなたが本当にしたい場合は、あなたがこれを行うことができます:collection.ToList().ForEach(c => { collection[collection.IndexOf(c)] = new <struct type>() { <propertyToSet> = value, <propertyToRetain> = c.Property2Retain }; });
ΕГИІИО

11
これには、新しいリストを作成するよりも、リストを適切に更新するというCameron MacFarlandの回答よりも優れています。
Simon Tewsi

7
うわー、この答えは本当に役に立ちません。ループを使用できるようにするためだけに新しいコレクションを作成する
Tim Schmelter 2017年

@SimonTewsiこれはオブジェクトのコレクションであるため、リストはとにかく更新する必要があります。コレクションは新しくなりますが、コレクション内のオブジェクトは同じになります。
Chris

70

私はこれをやっています

Collection.All(c => { c.needsChange = value; return true; });

これが最もクリーンな方法だと思います。
wcm 2013年

31
このアプローチは確かに機能しますが、All()拡張メソッドの意図に違反するため、他の誰かがコードを読み取るときに混乱を招く可能性があります。
トムバクスター

このアプローチの方が優れています。各ループを使用する代わりにすべてを使用
UJS

2
All()の使用目的について少し誤解を招く可能性がある場合でも、ToList()を不必要に呼び出すよりも間違いなくこれを優先してください。
iupchris10

1
そのようなコレクションを使用List<>している場合、このForEach()メソッドは、これを実現するための非常にわかりにくい方法です。例ForEach(c => { c.needsChange = value; })
DanはFirelightによって

27

私は実際に私が望んでいることをうまく行う拡張メソッド見つけました

public static IEnumerable<T> ForEach<T>(
    this IEnumerable<T> source,
    Action<T> act)
{
    foreach (T element in source) act(element);
    return source;
}

4
nice :) Lomaxx、例を追加して、覗き見で「アクション」(ブームティッシュ!)
Pure.Krome 2009

2
foreach(何らかの理由で)-ループを本当に避けたい場合は、これが唯一の便利なアプローチです。
Tim Schmelter 2017年

foreachコード自体にforeachループが含まれているため、まだ回避していない
@Rango

@GoldBishop確かに、このメソッドはループを隠します。
Tim Schmelter

1
リンクは壊れており、現在はcodewrecks.com/blog/index.php/2008/08/13/…で入手できます。stackoverflow.com/questions/200574にリンクするブログコメントもあります。次に、上位の質問コメントはblogs.msdn.microsoft.com/ericlippert/2009/05/18/…にリンクしてい ます。おそらく、答えはMSDNを使用してより簡単に書き直されます(必要に応じて、最初のリンクにクレジットを付けることもできます)。
サイドノート

15

使用する:

ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);

これがLINQを使いすぎているかどうかはわかりませんが、リスト内の特定のアイテムを特定の条件で更新する場合に役立ちます。


7

これを行うための組み込みの拡張メソッドはありません。定義はかなり簡単ですが。投稿の下部には、私が定義したIterateというメソッドがあります。そのまま使えます

collection.Iterate(c => { c.PropertyToSet = value;} );

ソースを繰り返す

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, (x, i) => callback(x));
}

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, callback);
}

private static void IterateHelper<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    int count = 0;
    foreach (var cur in enumerable)
    {
        callback(cur, count);
        count++;
    }
}

Iterateは必要ですか、Count、Sum、Avg、またはスカラー値を返すその他の既存の拡張メソッドの何が問題になっていますか?
AnthonyWJones 2008

2
これは私が欲しいものにかなり近いですが、少し関わっています。私が投稿したブログ投稿も同様の実装ですが、コードの行数は少なくなっています。
lomaxx 2008

1
IterateHelperはやり過ぎのようです。インデックスを取得しないオーバーロードは、より多くの余分な作業を行うことになります(コールバックをインデックスを取得するラムダに変換し、使用されないカウントを保持します)。私はそれが再利用であることを理解していますが、とにかくforloopを使用するだけの回避策なので、効率的です。
Cameron MacFarland

2
@ Cameron、IterateHelperには2つの目的があります。1)単一の実装、2)呼び出し時と使用時にArgumentNullExceptionがスローされるようにします。C#イテレータは遅延して実行され、ヘルパーを使用すると、反復中に例外がスローされるという奇妙な動作が防止されます。
JaredPar 2008

2
@JaredPar:イテレータを使用していない場合を除きます。利回りに関する記述はありません。
Cameron MacFarland

7

あなたは特にLINQソリューションを要求し、この質問はかなり古いですが、非LINQソリューションを投稿します。これは、LINQ(=言語統合クエリ)がコレクションのクエリに使用されることを意図しているためです。すべてのLINQメソッドは、基になるコレクションを変更せず、新しいコレクション(より正確には、新しいコレクションのイテレータ)を返すだけです。したがって、たとえばaを使って何をしSelectても、基礎となるコレクションには影響しません。新しいコレクションを取得するだけです。

もちろん、あなたは可能性でそれを行うForEach(どの方法で、LINQではありませんが、上の拡張List<T>)。しかし、これは文字通りforeachとにかく使用しますが、ラムダ式を使用します。これとは別に、すべての LINQメソッドは、たとえばforeachまたはを使用してコレクションを内部的に反復しますforが、クライアントからは単にそれを非表示にします。私はこれをこれ以上読みやすく、保守しやすいとは考えていません(ラムダ式を含むメソッドをデバッグしながらコードを編集することを考えてください)。

これは、コレクション内のアイテムを変更するためにLINQを使用するべきではないと述べました。より良い方法は、質問ですでに提供したソリューションです。クラシックループを使用すると、コレクションを簡単に反復してその項目を更新できます。実際、それらに依存するすべてのソリューションList.ForEachは何も変わらないが、私の観点から読むのははるかに難しい。

したがって、コレクションの要素を更新する必要がある場合は、LINQを使用しないでください。


3
オフトピック:同意します。LINQが悪用されている例、「高パフォーマンスLINQチェーン」を要求する人々の例、シングルループで実現できることを行う例などがあります。LINQを使用しないことはありがたいです。あまりにも私に染み付いており、通常は使用しません。LINQチェーンを使用して単一のアクションを実行している人がいます。LINQコマンドが使用されるたびに、「内部for」で別のループが作成されていることにほとんど気づいていません。標準的なコーディングの代わりになるのではなく、単純なタスクを実行する冗長でない方法を作成するのは、シンタティックシュガーだと思います。
ForeverZer0 2018

6

私はこれについていくつかのバリエーションを試しましたが、この男の解決策に戻ります。

http://www.hookedonlinq.com/UpdateOperator.ashx

繰り返しますが、これは他の誰かの解決策です。しかし、コードを小さなライブラリにコンパイルし、かなり定期的に使用しています。

彼のコードをここに貼り付けます。彼のサイト(ブログ)が将来存在しなくなる可能性があるためです。(「ここにあなたが必要とする正確な答えがあります」と書かれた投稿、クリック、そしてデッドURLを見ることほど悪いことはありません。)

    public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach (TSource element in source)
        {
            update(element);
            count++;
        }
        return count;
    }
}



int count = drawingObjects
        .Where(d => d.IsSelected && d.Color == Colors.Blue)
        .Update(e => { e.Color = Color.Red; e.Selected = false; } );

1
Action<TSource>追加のデリゲートを作成する代わりにを使用できます。それを書いている時点では、それは利用できなかったかもしれません。
フランクJ

ええ、それは当時の古い学校のDotNetでした。良いコメントフランク。
granadaCoder

URLは死んでいます!(このドメイン名は有効期限が切れています)良いコードをここにコピーしました!#patOnShoulder
granadaCoder

1
値の型をチェックすることは理にかなっていますが、おそらく、制約を使用すること、つまりwhere T: struct、コンパイル時にこれをキャッチすることをお勧めします。
Groo


3

私はそれを助けるためにいくつかの拡張メソッドを書きました。

namespace System.Linq
{
    /// <summary>
    /// Class to hold extension methods to Linq.
    /// </summary>
    public static class LinqExtensions
    {
        /// <summary>
        /// Changes all elements of IEnumerable by the change function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <returns>An IEnumerable with all changes applied</returns>
        public static IEnumerable<T> Change<T>(this IEnumerable<T> enumerable, Func<T, T> change  )
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");

            foreach (var item in enumerable)
            {
                yield return change(item);
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function, that fullfill the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeWhere<T>(this IEnumerable<T> enumerable, 
                                                    Func<T, T> change,
                                                    Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function that do not fullfill the except function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeExcept<T>(this IEnumerable<T> enumerable,
                                                     Func<T, T> change,
                                                     Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (!@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> Update<T>(this IEnumerable<T> enumerable,
                                               Action<T> update) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                update(item);
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// where the where function returns true
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where updates should be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateWhere<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                if (where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// Except the elements from the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateExcept<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");

            foreach (var item in enumerable)
            {
                if (!where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }
    }
}

私はそれを次のように使用しています:

        List<int> exampleList = new List<int>()
            {
                1, 2 , 3
            };

        //2 , 3 , 4
        var updated1 = exampleList.Change(x => x + 1);

        //10, 2, 3
        var updated2 = exampleList
            .ChangeWhere(   changeItem => changeItem * 10,          // change you want to make
                            conditionItem => conditionItem < 2);    // where you want to make the change

        //1, 0, 0
        var updated3 = exampleList
            .ChangeExcept(changeItem => 0,                          //Change elements to 0
                          conditionItem => conditionItem == 1);     //everywhere but where element is 1

参考までに、引数のチェック:

/// <summary>
/// Class for doing argument checks
/// </summary>
public static class ArgumentCheck
{


    /// <summary>
    /// Checks if a value is string or any other object if it is string
    /// it checks for nullorwhitespace otherwhise it checks for null only
    /// </summary>
    /// <typeparam name="T">Type of the item you want to check</typeparam>
    /// <param name="item">The item you want to check</param>
    /// <param name="nameOfTheArgument">Name of the argument</param>
    public static void IsNullorWhiteSpace<T>(T item, string nameOfTheArgument = "")
    {

        Type type = typeof(T);
        if (type == typeof(string) ||
            type == typeof(String))
        {
            if (string.IsNullOrWhiteSpace(item as string))
            {
                throw new ArgumentException(nameOfTheArgument + " is null or Whitespace");
            }
        }
        else
        {
            if (item == null)
            {
                throw new ArgumentException(nameOfTheArgument + " is null");
            }
        }

    }
}


2

LINQを使用してコレクションを配列に変換してから、Array.ForEach()を呼び出すことができます。

Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());

明らかに、これは構造体のコレクションや整数や文字列のような組み込み型では機能しません。


1

これが私が使用する拡張メソッドです...

    /// <summary>
    /// Executes an Update statement block on all elements in an  IEnumerable of T
    /// sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="action">The action method to execute for each element.</param>
    /// <returns>The number of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> action)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");
        if (typeof (TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        var count = 0;
        foreach (var element in source)
        {
            action(element);
            count++;
        }
        return count;
    }

なぜ「値型要素は更新でサポートされていません」?? 何も邪魔しません!
abatishchev 2010

それは私が取り組んでいたプロジェクトに固有のものでした。ほとんどの場合それは問題ではないと思います。最近、私はそれを作り直し、名前をRun(...)に変更し、値タイプのものを削除して、それがvoidを返すように変更し、カウントコードをドロップしました。
Bill Forney、

それも多かれ少なかれ何をするかですList<T>.ForEachが、すべてのためIEnumerableです。
HimBromBeere

これを振り返ってみると、foreachループを使用するだけです。このようなものを使用する唯一の利点は、メソッドを一緒にチェーン化し、関数から列挙可能なものを返し、アクションの実行後にチェーンを継続する場合です。それ以外の場合、これはメリットのない追加のメソッド呼び出しです。
Bill Forney、2018

0

クエリ内の値を変更して、関数を記述できるようにしたいと思います

void DoStuff()
{
    Func<string, Foo, bool> test = (y, x) => { x.Bar = y; return true; };
    List<Foo> mylist = new List<Foo>();
    var v = from x in mylist
            where test("value", x)
            select x;
}

class Foo
{
    string Bar { get; set; }
}

しかし、これがあなたの言っていることであるならば、無条件ではありません。


これは、vを列挙するために何かを必要とすることにより、正しい方向に進んでいます。そうでなければ、何もしません。
AnthonyWJones 2008


-3

以下のようなデータがあるとします。

var items = new List<string>({"123", "456", "789"});
// Like 123 value get updated to 123ABC ..

リストを変更し、リストの既存の値を変更された値に置き換える場合は、まず新しい空のリストを作成してから、各リストアイテムでmodifyメソッドを呼び出してデータリストをループします。

var modifiedItemsList = new List<string>();

items.ForEach(i => {
  var modifiedValue = ModifyingMethod(i);
  modifiedItemsList.Add(items.AsEnumerable().Where(w => w == i).Select(x => modifiedValue).ToList().FirstOrDefault()?.ToString()) 
});
// assign back the modified list
items = modifiedItemsList;

2
O(n)ランタイムで実行できるものをO(n ^ 2)以下で実行できるようにするのはなぜですか?IMはlinqの詳細がどのように機能するかを認識していませんが、これは最低でもn問題の^ 2ソリューションであることがわかります。
Fallenreaper
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.