web-dev-qa-db-fra.com

Mettre à jour tous les objets d'une collection à l'aide de LINQ

Y at-il un moyen de faire ce qui suit en utilisant LINQ?

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

Pour clarifier, je veux parcourir chaque objet d'une collection, puis mettre à jour une propriété sur chaque objet.

Mon cas d'utilisation est que j'ai plusieurs commentaires sur un article de blog et que je souhaite parcourir chaque commentaire d'un article de blog et définir la date et l'heure de ce dernier sur +10 heures. Je pourrais le faire en SQL, mais je veux le garder dans la couche de gestion.

406
lomaxx

Bien que vous puissiez utiliser une méthode d’extension ForEach, vous pouvez utiliser uniquement le framework.

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

La ToList est nécessaire pour évaluer la sélection immédiatement en raison de évaluation paresseuse .

713
Cameron MacFarland
collection.ToList().ForEach(c => c.PropertyToSet = value);
275
Ε Г И І И О

Je fais ça

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

J'ai en fait trouvé une méthode d'extension qui fera bien ce que je veux

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

Utilisation:

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

Je ne sais pas si LINUS a trop utilisé ou pas, mais cela a fonctionné pour moi lorsque je souhaite mettre à jour un élément spécifique de la liste pour une condition spécifique.

12
Hennish

Il n'y a pas de méthode d'extension intégrée pour le faire. Bien que définir soit assez simple. Au bas de l'article se trouve une méthode que j'ai définie et appelée Itérer. Il peut être utilisé comme tel

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

Itérer la source

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

J'ai essayé quelques variantes à ce sujet et je reviens sans cesse à la solution de ce type.

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

Encore une fois, c'est la solution de quelqu'un d'autre. Mais j'ai compilé le code dans une petite bibliothèque et je l'utilise assez régulièrement.

Je vais coller son code ici, car il est fort probable que son site (blog) cesse d'exister à l'avenir. (Il n'y a rien de pire que de voir un article qui dit "Voici la réponse exacte dont vous avez besoin", cliquez sur une URL morte.)

    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

Non, LINQ ne supporte pas une manière de mettre à jour en masse. Le seul moyen short serait d'utiliser une méthode d'extension ForEach - Pourquoi il n'y a pas de méthode d'extension ForEach sur IEnumerable?

3
Aaron Powell

Mes deux sous: -

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

Bien que vous ayez spécifiquement demandé une solution linq et que cette question soit assez ancienne, je publie une solution non-linq. En effet, linq (= lanuguage integrated query ) doit être utilisé pour les requêtes sur les collections. Toutes les méthodes linq ne modifient pas la collection sous-jacente, elles ne font que retourner une nouvelle (ou plus précisément un itérateur de nouvelle collection). Ainsi, quoi que vous fassiez, par exemple avec un Select n'affecte pas la collection sous-jacente, vous en obtenez simplement une nouvelle.

Bien sûr, vous pouvez le faire avec une ForEach (ce qui n’est pas linq, au fait, mais une extension sur List<T>). Mais ce littéralement _ utilise foreach de toute façon mais avec une expression lambda. En dehors de cela, every linq-method itère en interne votre collection, par exemple. en utilisant foreach ou for, mais il le cache simplement au client. Je ne considère pas cela comme plus lisible ni maintenable (pensez à éditer votre code lors du débogage d’une méthode contenant des expressions lambda).

Ceci dit, utilisez Linq pour modifier éléments de votre collection. Un meilleur moyen est la solution que vous avez déjà fournie dans votre question. Avec une boucle classique, vous pouvez facilement parcourir votre collection et mettre à jour ses éléments. En fait, toutes les solutions qui reposent sur List.ForEach ne sont pas différentes mais beaucoup plus difficiles à lire de mon point de vue.

Donc, vous ne devriez pas utiliser linq dans les cas où vous voulez mettre à jour les éléments de votre collection.

2
HimBromBeere

J'ai écrit quelques méthodes d'extension pour m'aider avec ça. 

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

Je l'utilise comme ceci:

        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

Pour référence la vérification d'argument:

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

Vous pouvez utiliser Magiq , un framework d’opérations par lots pour LINQ.

1
ivos

Voici la méthode d'extension que j'utilise ...

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

Vous pouvez utiliser LINQ pour convertir votre collection en tableau, puis appeler Array.ForEach ():

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

Évidemment, cela ne fonctionnera pas avec des collections de structures ou de types intégrés tels que des entiers ou des chaînes.

1
Tamas Czinege

Je suppose que vous voulez modifier les valeurs dans une requête pour pouvoir écrire une fonction correspondante

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

Mais pas si c'est ce que vous voulez dire.

0
Stormenet

Supposons que nous ayons des données comme ci-dessous,

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

et si nous voulons modifier la liste et remplacer les valeurs existantes de la liste par des valeurs modifiées, créez d'abord une nouvelle liste vide, puis parcourez la liste de données en appelant la méthode modifying sur chaque élément de la liste,

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