development

LINQ를 사용하여 목록에서 요소 제거

big-blog 2020. 10. 2. 22:54
반응형

LINQ를 사용하여 목록에서 요소 제거


다음과 같은 LINQ 쿼리가 있다고 가정합니다.

var authors = from x in authorsList
              where x.firstname == "Bob"
              select x;

그것이 authorsList유형 이라는 것을 감안할 때 쿼리에 의해 반환되는 요소를 List<Author>어떻게 삭제할 수 있습니까?AuthorauthorsListauthors

또는 다르게 말하면, 이름이 Bob과 같은 모든 이름을 어떻게 삭제할 수 authorsList있습니까?

참고 : 이것은 질문의 목적을위한 단순화 된 예입니다.


글쎄, 처음에는 그들을 제외하는 것이 더 쉬울 것입니다.

authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList();

그러나 authorsList이전 컬렉션에서 작성자를 제거하는 대신 값을 변경합니다 . 또는 다음을 사용할 수 있습니다 RemoveAll.

authorsList.RemoveAll(x => x.FirstName == "Bob");

다른 컬렉션을 기반으로 실제로해야하는 경우 HashSet, RemoveAll 및 Contains를 사용합니다.

var setToRemove = new HashSet<Author>(authors);
authorsList.RemoveAll(x => setToRemove.Contains(x));

작업을 수행 하려면 List <T> .RemoveAll사용하는 것이 좋습니다 .

authorsList.RemoveAll((x) => x.firstname == "Bob");

정말 항목을 제거해야한다면 Except ()는 어떻습니까?
새 목록을 기반으로 제거하거나 Linq를 중첩하여 즉석에서 제거 할 수 있습니다.

var authorsList = new List<Author>()
{
    new Author{ Firstname = "Bob", Lastname = "Smith" },
    new Author{ Firstname = "Fred", Lastname = "Jones" },
    new Author{ Firstname = "Brian", Lastname = "Brains" },
    new Author{ Firstname = "Billy", Lastname = "TheKid" }
};

var authors = authorsList.Where(a => a.Firstname == "Bob");
authorsList = authorsList.Except(authors).ToList();
authorsList = authorsList.Except(authorsList.Where(a=>a.Firstname=="Billy")).ToList();

LINQ는 업데이트 지원이 아닌 쿼리를 제공하기 때문에 표준 LINQ 연산자로는이 작업을 수행 할 수 없습니다.

그러나 새 목록을 생성하고 이전 목록을 바꿀 수 있습니다.

var authorsList = GetAuthorList();

authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList();

또는 authors두 번째 단계에서 모든 항목을 제거 할 수 있습니다.

var authorsList = GetAuthorList();

var authors = authorsList.Where(a => a.FirstName == "Bob").ToList();

foreach (var author in authors)
{
    authorList.Remove(author);
}

간단한 솔루션 :

static void Main()
{
    List<string> myList = new List<string> { "Jason", "Bob", "Frank", "Bob" };
    myList.RemoveAll(x => x == "Bob");

    foreach (string s in myList)
    {
        //
    }
}

RemoveAllExcept와 사용의 장점이 다른지 궁금해서 HashSet빠른 성능 검사를 수행했습니다. :)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ListRemoveTest
{
    class Program
    {
        private static Random random = new Random( (int)DateTime.Now.Ticks );

        static void Main( string[] args )
        {
            Console.WriteLine( "Be patient, generating data..." );

            List<string> list = new List<string>();
            List<string> toRemove = new List<string>();
            for( int x=0; x < 1000000; x++ )
            {
                string randString = RandomString( random.Next( 100 ) );
                list.Add( randString );
                if( random.Next( 1000 ) == 0 )
                    toRemove.Insert( 0, randString );
            }

            List<string> l1 = new List<string>( list );
            List<string> l2 = new List<string>( list );
            List<string> l3 = new List<string>( list );
            List<string> l4 = new List<string>( list );

            Console.WriteLine( "Be patient, testing..." );

            Stopwatch sw1 = Stopwatch.StartNew();
            l1.RemoveAll( toRemove.Contains );
            sw1.Stop();

            Stopwatch sw2 = Stopwatch.StartNew();
            l2.RemoveAll( new HashSet<string>( toRemove ).Contains );
            sw2.Stop();

            Stopwatch sw3 = Stopwatch.StartNew();
            l3 = l3.Except( toRemove ).ToList();
            sw3.Stop();

            Stopwatch sw4 = Stopwatch.StartNew();
            l4 = l4.Except( new HashSet<string>( toRemove ) ).ToList();
            sw3.Stop();


            Console.WriteLine( "L1.Len = {0}, Time taken: {1}ms", l1.Count, sw1.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L2.Len = {0}, Time taken: {1}ms", l1.Count, sw2.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L3.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L4.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );

            Console.ReadKey();
        }


        private static string RandomString( int size )
        {
            StringBuilder builder = new StringBuilder();
            char ch;
            for( int i = 0; i < size; i++ )
            {
                ch = Convert.ToChar( Convert.ToInt32( Math.Floor( 26 * random.NextDouble() + 65 ) ) );
                builder.Append( ch );
            }

            return builder.ToString();
        }
    }
}

아래 결과 :

Be patient, generating data...
Be patient, testing...
L1.Len = 985263, Time taken: 13411.8648ms
L2.Len = 985263, Time taken: 76.4042ms
L3.Len = 985263, Time taken: 340.6933ms
L4.Len = 985263, Time taken: 340.6933ms

보시다시피이 경우 가장 좋은 방법은 RemoveAll(HashSet)


이것은 매우 오래된 질문이지만 이것을 수행하는 정말 간단한 방법을 찾았습니다.

authorsList = authorsList.Except(authors).ToList();

반환 변수가 있기 때문에주의 authorsListList<T>IEnumerable<T>에 의해 반환이 Except()A를 변환해야합니다 List<T>.


You can remove in two ways

var output = from x in authorsList
             where x.firstname != "Bob"
             select x;

or

var authors = from x in authorsList
              where x.firstname == "Bob"
              select x;

var output = from x in authorsList
             where !authors.Contains(x) 
             select x;

I had same issue, if you want simple output based on your where condition , then first solution is better.


Say that authorsToRemove is an IEnumerable<T> that contains the elements you want to remove from authorsList.

Then here is another very simple way to accomplish the removal task asked by the OP:

authorsList.RemoveAll(authorsToRemove.Contains);

I think you could do something like this

    authorsList = (from a in authorsList
                  where !authors.Contains(a)
                  select a).ToList();

Although I think the solutions already given solve the problem in a more readable way.


Below is the example to remove the element from the list.

 List<int> items = new List<int>() { 2, 2, 3, 4, 2, 7, 3,3,3};

 var result = items.Remove(2);//Remove the first ocurence of matched elements and returns boolean value
 var result1 = items.RemoveAll(lst => lst == 3);// Remove all the matched elements and returns count of removed element
 items.RemoveAt(3);//Removes the elements at the specified index

i think you just have to assign the items from Author list to a new list to take that effect.

//assume oldAuthor is the old list
Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList();
oldAuthor = newAuthorList;
newAuthorList = null;

To keep the code fluent (if code optimisation is not crucial) and you would need to do some further operations on the list:

authorsList = authorsList.Where(x => x.FirstName != "Bob").<do_some_further_Linq>;

or

authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;

LINQ has its origins in functional programming, which emphasises immutability of objects, so it doesn't provide a built-in way to update the original list in-place.

Note on immutability (taken from another SO answer):

Here is the definition of immutability from Wikipedia.

In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created.


Is very simple:

authorsList.RemoveAll((x) => x.firstname == "Bob");

참고URL : https://stackoverflow.com/questions/853526/using-linq-to-remove-elements-from-a-listt

반응형