c# - new - Actualizar todos los objetos en una colección usando LINQ
linq foreach multiple actions (15)
Aquí está el método de extensión que utilizo ...
/// <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;
}
¿Hay alguna manera de hacer lo siguiente usando LINQ?
foreach (var c in collection)
{
c.PropertyToSet = value;
}
Para aclarar, quiero iterar a través de cada objeto en una colección y luego actualizar una propiedad en cada objeto.
Mi caso de uso es que tengo un montón de comentarios en una publicación de blog, y quiero repetir cada comentario en una publicación de blog y establecer la fecha y hora en la publicación de blog en 10 horas. Podría hacerlo en SQL, pero quiero mantenerlo en la capa empresarial.
Aunque solicitó específicamente una solución de linq y esta pregunta es bastante antigua, publico una solución que no es de linq. Esto se debe a que linq (= lanuguage Integrated query ) se utiliza para consultas en colecciones. Todos los métodos linq no modifican la colección subyacente, solo devuelven una nueva (o, más precisamente, un iterador a una nueva colección). Por lo tanto, lo que haga, por ejemplo, con un Select
no afecta a la colección subyacente, simplemente obtiene uno nuevo.
Por supuesto, puedes hacerlo con un ForEach
(que no es linq, por cierto, sino una extensión en la List<T>
). Pero esto literalmente usa foreach
todos modos pero con una expresión lambda. Aparte de esto, cada método linq internamente itera su colección, por ejemplo, utilizando foreach
o for
, sin embargo, simplemente la oculta del cliente. No considero que esto sea más fácil de leer o mantener (piense en editar su código mientras depura un método que contiene expresiones lambda).
Dicho esto, no debe utilizar Linq para modificar los elementos de su colección. Una mejor manera es la solución que ya proporcionó en su pregunta. Con un bucle clásico puede iterar fácilmente su colección y actualizar sus artículos. De hecho, todas esas soluciones basadas en List.ForEach
no son nada diferentes, pero son mucho más difíciles de leer desde mi perspectiva.
Así que no debes usar linq en aquellos casos en los que quieras actualizar los elementos de tu colección.
De hecho, encontré un método de extensión que hará lo que quiero bien
public static IEnumerable<T> ForEach<T>(
this IEnumerable<T> source,
Action<T> act)
{
foreach (T element in source) act(element);
return source;
}
Escribí algunos métodos de extensión para ayudarme con eso.
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;
}
}
}
Lo estoy usando así:
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
Para referencia el argumento de verificación:
/// <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");
}
}
}
}
He intentado algunas variaciones en esto, y sigo volviendo a la solución de este tipo.
http://www.hookedonlinq.com/UpdateOperator.ashx
Una vez más, esta es la solución de alguien más. Pero he compilado el código en una pequeña biblioteca y lo uso con bastante regularidad.
Voy a pegar su código aquí, por la remota posibilidad de que su sitio (blog) deje de existir en algún momento en el futuro. (No hay nada peor que ver una publicación que dice "Aquí está la respuesta exacta que necesitas", clic y URL muerta).
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; } );
Mis 2 centavos: -
collection.Count(v => (v.PropertyToUpdate = newValue) == null);
No hay un método de extensión incorporado para hacer esto. Aunque la definición de uno es bastante sencillo. En la parte inferior de la publicación hay un método que definí llamado Iterate. Se puede usar como tal.
collection.Iterate(c => { c.PropertyToSet = value;} );
Fuente Iterada
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++;
}
}
No, LINQ no admite una forma de actualización masiva. La única forma más corta sería utilizar un método de extensión ForEach
. ¿Por qué no hay un método de extensión ForEach en IEnumerable?
Puede usar Magiq , un marco de operación por lotes para LINQ.
Puedes usar LINQ para convertir tu colección en una matriz y luego invocar Array.ForEach ():
Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());
Obviamente, esto no funcionará con colecciones de estructuras o tipos incorporados como enteros o cadenas.
Si bien puede usar un método de extensión ForEach
, si desea usar solo el marco puede hacerlo
collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();
El ToList
es necesario para evaluar la selección inmediatamente debido a la evaluación perezosa .
Supongo que desea cambiar los valores dentro de una consulta para poder escribir una función para ella
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; }
}
Pero no sé si esto es lo que quieres decir.
Utilizar:
ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);
No estoy seguro de si esto es un uso excesivo de LINQ o no, pero me ha funcionado al querer actualizar un artículo específico en la lista para una condición específica.
yo estoy haciendo esto
Collection.All(c => { c.needsChange = value; return true; });
collection.ToList().ForEach(c => c.PropertyToSet = value);