it-swarm.com.de

Aktualisieren Sie alle Objekte in einer Sammlung mit LINQ

Gibt es eine Möglichkeit, mit LINQ Folgendes zu tun?

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

Zur Verdeutlichung möchte ich jedes Objekt in einer Auflistung durchlaufen und dann eine Eigenschaft für jedes Objekt aktualisieren.

Mein Anwendungsfall ist, dass ich eine Reihe von Kommentaren zu einem Blogeintrag habe. Ich möchte jeden Kommentar eines Blogeintrags durchlaufen und die Datumszeit im Blogeintrag auf +10 Stunden einstellen. Ich könnte es in SQL machen, aber ich möchte es in der Business-Schicht behalten.

406
lomaxx

Sie können zwar eine ForEach-Erweiterungsmethode verwenden, wenn Sie nur das Framework verwenden möchten, das Sie ausführen können

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

Die ToList wird benötigt, um die Auswahl aufgrund von Lazy Evaluation sofort auszuwerten.

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

Ich mache das

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

Ich habe tatsächlich eine Erweiterungsmethode gefunden die das tun wird, was ich will

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

Benutzen:

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

Ich bin nicht sicher, ob dies LINQ zu stark beansprucht oder nicht, aber es hat sich für mich bewährt, wenn ich bestimmte Elemente in der Liste für eine bestimmte Bedingung aktualisieren möchte.

12
Hennish

Dafür gibt es keine integrierte Erweiterungsmethode. Obwohl das Definieren eines recht einfach ist. Am Ende des Beitrags befindet sich eine Methode, die ich als Iterate bezeichnete. Es kann so verwendet werden

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

Quelle iterieren

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++;
    }
}
6
JaredPar

Ich habe ein paar Variationen dazu ausprobiert und gehe immer wieder zur Lösung dieses Mannes.

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

Auch dies ist die Lösung eines anderen. Aber ich habe den Code in eine kleine Bibliothek kompiliert und verwende ihn ziemlich regelmäßig.

Ich werde seinen Code hier einfügen, für den zufälligen Fall, dass seine Website (Blog) irgendwann in der Zukunft nicht mehr existiert. (Es gibt nichts Schlimmeres, als einen Beitrag zu lesen, in dem "Hier ist genau die Antwort, die Sie brauchen", Klick und Tote URL lautet.)

    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; } );
5
granadaCoder

Nein, LINQ unterstützt keine Massenaktualisierung. Der einzige kürzer Weg wäre die Verwendung einer Erweiterungsmethode ForEach - Warum gibt es keine ForEach-Erweiterungsmethode für IEnumerable?

3
Aaron Powell

Meine 2 Pfennige: -

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

Obwohl Sie speziell nach einer Linq-Lösung gefragt haben und diese Frage ziemlich alt ist, poste ich eine Nicht-Linq-Lösung. Dies liegt daran, dass linq (= lanuguage integrated query) für Abfragen von Sammlungen verwendet wird. Alle linq-Methoden modifizieren die zugrunde liegende Sammlung nicht, sondern return eine neue (oder genauer ein Iterator zu einer neuen Sammlung). Was immer Sie also tun, z. Wenn ein Select die zugrunde liegende Sammlung nicht beeinflusst, erhalten Sie einfach eine neue.

Natürlich können Sie könnte es mit einem ForEach machen (das ist übrigens keine Linq, sondern eine Erweiterung auf List<T>). Aber dies wörtlich verwendet foreach trotzdem, aber mit einem Lambda-Ausdruck. Abgesehen davon durchläuft every die linq-Methode Ihre Sammlung intern, z. Mit foreach oder for wird es jedoch einfach vor dem Client ausgeblendet. Ich halte dies weder für lesbar noch für wartbar (denken Sie daran, Ihren Code zu bearbeiten, während Sie eine Methode mit Lambda-Ausdrücken debuggen).

Allerdings sollten Sie Linq nicht verwenden, um modify Elemente in Ihrer Sammlung zu bearbeiten. Ein besserer Weg ist die Lösung, die Sie bereits in Ihrer Frage angegeben haben. Mit einer klassischen Schleife können Sie Ihre Sammlung problemlos durchlaufen und ihre Elemente aktualisieren. In der Tat alle diese Lösungen unter Berufung auf List.ForEach sind nichts anderes, aber aus meiner Sicht weitaus schwerer zu lesen.

Daher sollten Sie linq nicht in den Fällen verwenden, in denen Sie update die Elemente Ihrer Sammlung verwenden möchten.

2
HimBromBeere

Ich habe einige Erweiterungsmethoden geschrieben, um mir dabei zu helfen. 

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 ([email protected](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;
        }
    }
}

Ich benutze es so:

        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

Zur Referenz das Argument check:

/// <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
PartTimeIndie

Sie können Magiq verwenden, ein Batch-Operations-Framework für LINQ.

1
ivos

Hier ist die Erweiterungsmethode, die ich verwende ...

    /// <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;
    }
1
Bill Forney

Sie können LINQ verwenden, um Ihre Auflistung in ein Array zu konvertieren und dann Array.ForEach () aufzurufen:

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

Offensichtlich funktioniert dies nicht mit Sammlungen von Strukturen oder eingebauten Typen wie Ganzzahlen oder Strings.

1
Tamas Czinege

Ich gehe davon aus, dass Sie Werte innerhalb einer Abfrage ändern möchten, damit Sie eine Funktion dafür schreiben können

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

Aber nicht sicher, ob Sie das meinen.

0
Stormenet

Angenommen, wir haben Daten wie unten,

var items = new List<string>({"123", "456", "789"});

und wenn wir die Liste ändern und die vorhandenen Werte der Liste in geänderte Werte ersetzen möchten, erstellen Sie zuerst eine neue leere Liste und durchlaufen Sie dann die Datenliste, indem Sie die modifizierende Methode für jedes Listenelement aufrufen.

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;
0
Vishwa G