LINQ를 사용하여 List <T>에서 요소 제거


654

다음과 같은 LINQ 쿼리가 있다고 가정하십시오.

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

그것이 authorsList유형 이라면, 쿼리에 의해 반환 된 요소를 List<Author>어떻게 삭제할 수 있습니까?AuthorauthorsListauthors

또는 다른 방법으로, 이름의 동일한 Bob을 모두에서 어떻게 삭제할 수 authorsList있습니까?

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

답변:


1138

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

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

14
다른 컬렉션에 HashSet을 사용하는 이유는 무엇입니까?
123 456 789 0

54
@LeoLuis : Contains확인 속도가 빠르고 시퀀스를 한 번만 평가할 수 있습니다.
Jon Skeet 2012 년

2
@LeoLuis : 예, 시퀀스에서 HashSet을 빌드하면 한 번만 평가됩니다. "약한 컬렉션 집합"이 무슨 뜻인지 잘 모르겠습니다.
Jon Skeet

2
@ AndréChristofferAndersen : "오래된"은 무슨 뜻입니까? 여전히 작동합니다. 당신이 있다면 List<T>그것을 사용하는 것이 좋습니다.
Jon Skeet

4
@ AndréChristofferAndersen : 사용하는 것이 더 좋을 것이다authorsList = authorsList.Where(x => x.FirstName != "Bob")
존 소총

133

이를 위해서는 List <T> .RemoveAll 을 사용하는 것이 좋습니다 .

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

8
@Reed Copsey : 예제의 람다 매개 변수는 괄호로 묶습니다 (예 : (x)). 이에 대한 기술적 이유가 있습니까? 좋은 습관으로 간주됩니까?
매트 데이비스

24
아니요.> 1 매개 변수가 필요합니다. 단일 매개 변수를 사용하면 선택 사항이지만 일관성을 유지하는 데 도움이됩니다.
Reed Copsey

48

실제로 항목을 제거 해야하는 경우 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();

Except()LINQ 문 중간에 갈 수있는 유일한 방법입니다. IEnumerable이 없습니다 Remove()RemoveAll().
Jari Turkia

29

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

12
RemoveAll()LINQ 연산자가 아닙니다.
Daniel Brückner

사과드립니다. 당신은 100 % 정확합니다. 불행히도, 나는 downvote를 되돌릴 수 없다. 미안합니다.
Shai Cohen

Remove또한 System.Linq.Enumerable 메소드가 아닌 List< T> 메소드입니다.
DavidRR

@Daniel, 내가 틀렸다면 정정하십시오. 두 번째 옵션에 대한 조건에서 .ToList ()를 피할 수 있습니다. 즉 아래 코드가 작동합니다. var authorsList = GetAuthorList (); var authors = authorsList.Where (a => a.FirstName == "Bob"); foreach (작가의 작성자) {authorList.Remove (작성자); }
Sai

예, 이것이 작동합니다. 목록을 목록으로 바꾸려면 목록을 일부 방법으로 전달하거나 나중에 더 많은 항목을 추가하거나 제거하려는 경우에만 필요합니다. 시퀀스를 여러 번 열거 해야하는 경우에도 유용 할 수 있습니다. 왜냐하면 잠재적으로 비싼 조건을 한 번만 평가하거나 결과가 두 열거 사이에서 변경 될 수있는 경우 (예 : 조건이 현재 시간에 의존하기 때문에) 하나의 루프에서만 사용하려면 먼저 결과를 목록에 저장할 필요가 없습니다.
Daniel Brückner

20

간단한 해결책 :

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

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

"밥"과 "제이슨"을 제거하는 방법 문자열 목록에서 여러 개를 의미합니까?
Neo

19

RemoveAll와 사용하는 사람 Except과 사용하는 사람 사이에 차이가 있는지 궁금 하여 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)


이 코드는 "l2.RemoveAll (new HashSet <string> (toRemove) .Contains);" 컴파일해서는 안됩니다 ... 테스트가 정확하면 Jon Skeet이 이미 제안한 것보다 두 번째입니다.
파스칼

2
l2.RemoveAll( new HashSet<string>( toRemove ).Contains );FYI 만 컴파일
AzNjoE

9

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

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

반환 변수 authorsList는 a List<T>이므로로 IEnumerable<T>반환되는 변수 는 Except()로 변환되어야합니다 List<T>.


7

두 가지 방법으로 제거 할 수 있습니다

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

또는

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

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

where condition을 기반으로 간단한 출력을 원한다면 첫 번째 솔루션이 더 좋습니다.


"밥"또는 "빌리"는 어떻게 확인할 수 있습니까?
Si8

6

authorsToRemove입니다 IEnumerable<T>당신이에서 제거 할 요소를 포함하는 authorsList.

다음은 OP가 요청한 제거 작업을 수행하는 또 다른 매우 간단한 방법입니다.

authorsList.RemoveAll(authorsToRemove.Contains);

5

나는 당신이 이런 식으로 할 수 있다고 생각합니다

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

이미 제공된 솔루션이 더 읽기 쉬운 방식으로 문제를 해결한다고 생각합니다.


4

아래는 목록에서 요소를 제거하는 예입니다.

 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

1

LINQ는 객체의 불변성을 강조하는 기능적 프로그래밍의 기원을 가지고 있으므로 원래 목록을 제자리에서 업데이트하는 기본 방법을 제공하지 않습니다.

불변성에 대한 참고 사항 (다른 SO 답변에서 가져옴) :

다음은 Wikipedia 의 불변성 정의입니다 .

객체 지향 및 기능적 프로그래밍에서 불변 객체는 생성 된 후 상태를 수정할 수없는 객체입니다.


0

저자 목록의 항목을 새 목록에 할당해야 효과가 나타납니다.

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

0

코드를 유창하게 유지하려면 (코드 최적화가 중요하지 않은 경우) 목록에서 추가 작업을 수행해야합니다.

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

또는

authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.