395

LINQを使用して次のことを行う方法はありますか?

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

わかりやすくするために、コレクション内の各オブジェクトを繰り返し処理してから、各オブジェクトのプロパティを更新します。

私のユースケースは、私がブログ記事にたくさんのコメントを持っているということです、そして、私はブログ記事のそれぞれのコメントを通して反復して、+10時間にブログ記事の日時を設定したいです。私はSQLでそれをすることができました、しかし私はビジネス層でそれを保ちたいです。


  • 興味深い質問です。個人的には、私はあなたがどのようにしてそれを上回ったのかを好む。 - noelicus
  • 私はここで同じ質問に対する答えを探していましたが、将来の開発者があなたのOPで行ったのと同じやり方でそれをやるのも同じくらい簡単で、コードが少なく、理解しやすいと決心しました。 - Casey Crookston
  • なぜあなたはLINQでそれをしたいのですか? - Caltor
  • この質問では間違ったことを求められますが、唯一の正しい答えは次のとおりです。LINQを使用してデータソースを変更しないでください。 - Rango

15 답변


689

あなたが使用することができますがForEachあなたができるフレームワークだけを使いたいのであれば、拡張メソッド

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

ToListすぐにselectを評価するために必要です。遅延評価


  • 私はこれを支持しました。それがとても良い解決策だからです。私が拡張方法を好む唯一の理由はそれが起こっていることを正確に理解することをもう少し明確にすることです。 - lomaxx
  • コレクションがObservableCollectionたとえば、新しいリストを作成するのではなく、その場でアイテムを変更すると便利です。 - Cameron MacFarland
  • @desaivvええ、これはちょっと構文の乱用なので、Resharperはこれについて警告しています。 - Cameron MacFarland
  • 私見、これは単純なforeachループよりはるかに表現力が低いです。 ToList()は混乱を招くものです。これは、それ以外では延期される評価を強制すること以外には使用されないためです。この投影法は、本来の目的には使用されていないため、混乱を招きます。代わりに、コレクションの要素を繰り返し処理し、プロパティへのアクセスを許可してプロパティを更新できるようにします。私の頭に浮かぶ唯一の問題は、foreachループがParallel.ForEachを使った並列処理から恩恵を受けることができるかどうかということでしょう、しかしそれは別の問題です。 - Philippe
  • 誰かがなぜこれが最高の投票された答えなのか説明できますか?なぜこれがForEachを使うより良いのでしょうか。 - Igor Meszaros

263

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


  • 複数の物件を更新するにはどうすればいいですか? - iamCR
  • @SanthoshKumar:使用するcollection.ToList().ForEach(c => { c.Property1ToSet = value1; c.Property2ToSet = value2; }); - Ε Г И І И О
  • 私はVB.NETを使用しています、これは私のために働いていません:( - kbvishnu
  • これは、新しいリストを作成するのではなく、適切な場所でリストを更新するというCameron MacFarlandの回答よりも優れています。 - Simon Tewsi
  • うわー、この答えは本当に役に立ちません。ループを使えるようにするためだけに新しいコレクションを作成する - Rango

56

私はこれをやっています

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


  • これが最もきれいな方法だと思います。 - wcm
  • このアプローチは確かに機能しますが、それはユーザーの意図に違反します。All()他の誰かがコードを読んだときに混乱を招く可能性がある拡張方法。 - Tom Baxter
  • このアプローチはより良いです。各ループを使う代わりにAllを使う - UJS
  • All()の使用目的について少し誤解を招く可能性がある場合でも、ToList()を不必要に呼び出すよりも、これを絶対にお勧めします。 - iupchris10

19

私は実際に拡張メソッドを見つけましたそれは私がうまく欲しいことをするでしょう

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


  • いいね:) Lomaxx、'アクション'で見られるように例を追加してください。 (ブームティッシュ!) - Pure.Krome
  • これを回避したい場合は、これが唯一の有用なアプローチです。foreach - ループ(なんらかの理由で) - Rango
  • あなたがまだ避けていない@Rangoforeachコード自体にforeachループ - GoldBishop
  • @GoldBishop確実に、メソッドはループを隠します。 - Rango

12

つかいます:

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

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


5

これを行うための組み込みの拡張方法はありません。定義するのはかなり簡単ですが。記事の一番下に、私が定義したメソッド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++;
    }
}


  • Count、Sum、Avg、またはスカラー値を返すその他の既存の拡張方法では問題ありません。 - AnthonyWJones
  • これは私が欲しいものにかなり近いですが少し..関与しています。私が投稿したブログ投稿も同様の実装をしていますが、コード行数が少なくなっています。 - lomaxx
  • IterateHelperはやり過ぎです。インデックスを受け取らないオーバーロードは、より多くの余分な作業を実行することになります(インデックスを受け取るラムダにコールバックを変換し、決して使われないカウントを保持します)。私はそれが再利用されていることを理解していますが、それはとにかくforloopを使用するための回避策ですので効率的であるべきです。 - Cameron MacFarland
  • @Cameron、IterateHelperは2つの目的を果たします。 1)単一の実装と2)ArgumentNullExceptionを呼び出し時と使用時の両方でスローすることができます。 C#イテレータは実行が遅れるため、ヘルパーを使用することで、反復中に例外がスローされるという奇妙な動作を防ぐことができます。 - JaredPar
  • @JaredPar:イテレータを使用していない場合を除きます。 yieldステートメントはありません。 - Cameron MacFarland

4

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

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

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

彼のサイト(ブログ)が将来のある時点で存在しなくなる可能性があるため、ここに彼のコードを貼り付けます。 (「ここにあなたが必要とする正確な答えがあります」と書かれた、クリックして、そしてDead 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; } );


  • あなたが使用することができますAction<TSource>追加のデリゲートを作成する代わりに。しかし、それを書いている時点では利用できなかったかもしれません。 - Frank J

3

いいえ、LINQは一括更新の方法をサポートしていません。唯一のより短い方法はForEach拡張方法 - IEnumerableにForEach拡張メソッドがないのはなぜですか?


3

私の2ペニー: -

 collection.Count(v => (v.PropertyToUpdate = newValue) == null);


  • 私はその考え方が好きですが、コードが何をしているのか明確ではありません - lomaxx
  • ええ、実運用コードでこれが欲しいのかどうかわかりません。 - Cameron MacFarland

2

私はそれを手助けするためにいくつかの拡張方法を書きました。

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-solutionを求めていて、この質問はかなり古いですが、私はnon-linq-solutionを投稿しています。これはlinq(= lanuguageが統合されているためです問い合わせ)コレクションに対する問い合わせに使用されるべきです。すべてのlinqメソッドは、基礎となるコレクションを修正しません。戻る新しいもの(より正確には新しいコレクションへのイテレータ)したがって、あなたがすることは何でも。とともにSelect基礎となるコレクションには影響しません。単に新しいコレクションを入手するだけです。

もちろんあなたできたでそれを行うForEach(ところで、これはlinqではありませんが、拡張子はList<T>)でも、これ文字通り用途foreachとにかくラムダ式ですが。これから離れてすべてのlinq-methodはコレクションを内部的に繰り返します。を使ってforeachまたはforしかし、それは単にクライアントからそれを隠します。これ以上読みやすく保守しやすいとは考えていません(ラムダ式を含むメソッドをデバッグしながらコードを編集することは考えないでください)。

この肩はLinqを使って修正するあなたのコレクションの中のアイテム。もっと良い方法はあなたがすでにあなたの質問で提供した解決策です。古典的なループを使用すると、コレクションを簡単に繰り返してそのアイテムを更新できます。実際には、これらすべての解決策に頼っていますList.ForEach何も変わらないが、私の観点から読むのははるかに難しい。

だからあなたがしたい場合にはlinqを使うべきではない更新あなたのコレクションの要素


  • 話題外:私は同意します。LINQが悪用されるケースは非常に多いので、「高性能LINQチェーン」を要求する人々の例で、1つのループでできることなどを実行します。LINQを使用しないことはありがたいです。私にはあまりにも深く根付いていて、通常は使わないでください。 LINQコマンドを使用するたびに別のアクションを作成していることに気付いていないのに、LINQチェーンを使用して単一のアクションを実行する人たちがいるようです。for「フードの下に」ループします。標準的なコーディングに代わるものではなく、単純なタスクを実行するための冗長度の低い方法を作成することは構文上の砂糖だと思います。 - ForeverZer0

1

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;
    }


  • なぜ「value type要素はupdateでサポートされていません」???それを妨げるものは何もありません。 - abatishchev
  • それは私が取り組んでいたプロジェクトに特有のものでした。ほとんどの場合、問題にならないと思います。最近私はそれを作り直してRun(...)という名前に変更し、value型のものを削除し、voidを返すように変更してカウントコードを削除しました。 - Bill Forney
  • それはReactive extensions Runメソッドとそれを揃えます... - Bill Forney
  • 多かれ少なかれそれはList<T>.ForEachまた、すべての人のためにIEnumerable。 - HimBromBeere
  • これを今振り返ってみると、foreachループを使用することをお勧めします。このようなものを使用する唯一の利点は、メソッドをまとめて、アクションの実行後にチェーンを続行するために関数から列挙型を返したい場合です。そうでなければ、これは利点のない単なる追加のメソッド呼び出しです。 - Bill Forney

1

あなたが使用することができますマギクLINQのバッチ操作フレームワーク。


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

リンクされた質問


関連する質問

最近の質問