programing

LINQ를 사용하여 컬렉션의 모든 개체 업데이트

sourcejob 2023. 5. 4. 19:54
반응형

LINQ를 사용하여 컬렉션의 모든 개체 업데이트

LINQ를 사용하여 다음을 수행할 수 있는 방법이 있습니까?

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

명확하게 하기 위해 컬렉션의 각 개체를 반복한 다음 각 개체의 속성을 업데이트합니다.

저의 활용 사례는 블로그 게시물에 많은 댓글이 있는데, 블로그 게시물의 각 댓글을 반복하여 블로그 게시물의 날짜를 +10시간으로 설정하고 싶습니다.SQL에서 할 수 있지만 비즈니스 계층에서 유지하고 싶습니다.

동있안는을 할 수 동안.ForEach 방법,, 은 할 수 있습니다, , 수약워크만사용싶다확고면하임.

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

ToList게으른 평가로 인해 선택 항목을 즉시 평가하기 위해 필요합니다.

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

나는 이것을

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

실제로 내가 원하는 것을 멋지게 수행할 확장 방법을 찾았습니다.

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

사용:

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

이것이 LINQ를 과도하게 사용하는 것인지 아닌지는 잘 모르겠지만, 특정 조건에 대해 목록의 특정 항목을 업데이트하고 싶을 때 효과가 있었습니다.

당신이 LINQ 솔루션을 구체적으로 요청했고 이 질문은 꽤 오래되었지만 저는 비 LINQ 솔루션을 게시합니다.이는 LINQ(= 언어 통합 쿼리)가 컬렉션의 쿼리에 사용되도록 되어 있기 때문입니다.모든 LINQ 메서드는 기본 컬렉션을 수정하지 않고 새 컬렉션(또는 더 정확한 반복기를 새 컬렉션으로 반환합니다.그러므로 당신이 무엇을 하든지 간에. 예를 들어.Select기본 컬렉션에 영향을 미치지 않고 새 컬렉션을 얻을 수 있습니다.

물론 당신은 그것을 할 수 있습니다.ForEach (LINQ, 그건가아라니, ▁(▁on▁extension▁(다참▁isn니,입의장)의 확장자입니다.List<T>하지만 이것은 문자 그대로 사용됩니다.foreach어쨌든, 하지만 람다-표현으로. 외에도 모든 LINQ 방법은 내부적으로 다음과 같은 방법으로 수집을 반복합니다.foreach또는for하지만 그것은 단순히 그것을 고객에게 숨깁니다.저는 이것이 더 이상 읽기 쉽거나 유지 가능하다고 생각하지 않습니다(람다 표현식을 포함하는 메소드를 디버깅하는 동안 코드를 편집하는 것을 생각해 보십시오).

하지만 LINQ를 사용하여 컬렉션의 항목을 수정하면 안 됩니다.더 나은 방법은 질문에서 이미 제공한 솔루션입니다.클래식 루프를 사용하면 컬렉션을 쉽게 반복하고 항목을 업데이트할 수 있습니다.실제로 이러한 모든 솔루션은 다음과 같은 이점을 제공합니다.List.ForEach다름이 아니라 제 관점에서 읽기가 훨씬 어렵습니다.

따라서 컬렉션의 요소를 업데이트하려는 경우 LINQ를 사용하면 안 됩니다.

이를 위한 기본 제공 확장 방법은 없습니다.하나를 정의하는 것은 꽤 간단합니다.게시물 하단에는 제가 정의한 반복이라는 방법이 있습니다.이렇게 사용할 수 있습니다.

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

저는 이것에 대해 몇 가지 변형을 시도했고, 계속해서 이 사람의 해결책으로 돌아갑니다.

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

아니요, LINQ는 대량 업데이트 방식을 지원하지 않습니다.더 짧은 유일한 방법은 다음을 사용하는 것입니다.ForEach확장 방법 - IEnumberable에 ForEverse 확장 방법이 없는 이유는 무엇입니까?

어떤 사람들은 이것을 논평이라고 생각하지만, 저에게는 답입니다. 왜냐하면 잘못된 일을 하는 올바른 방법은 그것을 하지 않는 것이기 때문입니다.그래서 이 질문에 대한 답은 질문 자체에 있습니다.

LINQ를 사용하여 데이터를 수정하지 마십시오.루프를 사용합니다.

LINQ를 사용하여 컬렉션을 배열로 변환한 다음 배열을 호출할 수 있습니다.각():

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

분명히 이것은 구조체 모음이나 정수 또는 문자열과 같은 내장형 유형에서는 작동하지 않습니다.

저는 그것을 도와주기 위해 확장 방법을 몇 가지 썼습니다.

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

    }
}

다음은 제가 사용하는 확장 방법입니다...

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

LINQ의 배치 작업 프레임워크인 Magiq를 사용할 수 있습니다.

쿼리에 대한 함수를 작성할 수 있도록 쿼리 내부의 값을 변경하려고 합니다.

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

하지만 이것이 당신이 의미하는 것인지 확신할 수 없습니다.

나의 2페니:-

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

Adi Lester의 대답을 인용합니다(https://stackoverflow.com/a/5755487/8917485)

저는 이 대답이 꽤 마음에 드는데, 이 대답은 버그가 있습니다.새로 만든 목록의 값만 변경합니다.실제 변경된 목록을 읽으려면 두 줄로 변경해야 합니다.

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

아래와 같은 데이터가 있다고 가정해 보겠습니다.

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

목록을 수정하고 목록의 기존 값을 수정된 값으로 바꾸려면 먼저 새 빈 목록을 만든 다음 각 목록 항목에서 수정 방법을 호출하여 데이터 목록을 순환합니다.

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;

언급URL : https://stackoverflow.com/questions/398871/update-all-objects-in-a-collection-using-linq

반응형