programing

반복하는 동안 일반 목록에서 요소를 제거하려면 어떻게 해야 합니까?

skycolor 2023. 4. 17. 21:38
반응형

반복하는 동안 일반 목록에서 요소를 제거하려면 어떻게 해야 합니까?

각각 처리해야 할 요소 목록으로 작업하고 결과에 따라 목록에서 삭제하기 위해 더 나은 패턴을 찾고 있습니다.

하면 안 요..Remove(element) a foreach (var element in X) (가칭)이 되기 에,Collection was modified; enumeration operation may not execute. 역시 못쓰게 됩니다.for (int i = 0; i < elements.Count(); i++) ★★★★★★★★★★★★★★★★★」.RemoveAt(i) 지금 하고 있는 입니다.i.

우아한 방법이 없을까요?

for 루프를 사용하여 목록을 반대로 반복합니다.

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

예:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

또는 술어와 함께 RemoveAll 메서드를 사용하여 다음을 테스트할 수 있습니다.

safePendingList.RemoveAll(item => item.Value == someValue);

다음은 간단한 예시를 보여드리겠습니다.

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));
 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

「」를 붙이면,목록(또는 LINQ 쿼리 결과)에 대한 ToList()는 "Collection was modified; enumeration operation may not execute." 오류 없이 "list"에서 "item"을 직접 제거할 수 있습니다.컴파일러는 "list"를 복사하기 때문에 어레이에서 안전하게 삭제할 수 있습니다.

패턴은 매우 효율적이지는 않지만 자연스러운 느낌과 거의 모든 상황에서 충분히 유연합니다.예를 들어, 각 "항목"을 DB에 저장하고 DB 저장이 성공한 경우에만 목록에서 제거할 수 있습니다.

심플하고 간단한 솔루션:

컬렉션에서 역방향으로 실행되는 표준 for-loop을 사용합니다.RemoveAt(i)요소를 제거합니다.

컬렉션에서 반복하는 동안 컬렉션에서 요소를 제거할 때 가장 먼저 떠오르는 것은 역반복입니다.

운 좋게도, 불필요한 타이핑이 수반되어 오류가 발생하기 쉬운 for 루프를 쓰는 것보다 더 우아한 해결책이 있습니다.

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

범용 목록에서 ToArray()를 사용하면 범용 목록에서 제거(항목)를 수행할 수 있습니다.

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

원하지 않는 요소를 제거하지 말고 원하는 요소를 선택하십시오.이 방법은 요소를 제거하는 것보다 훨씬 쉽고 일반적으로 효율적입니다.

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

아래에 Michael Dillon이 남긴 코멘트에 대한 답변으로 이 글을 투고하고 싶었지만, 어쨌든 제 답변에 넣기에는 너무 길고 도움이 될 것 같습니다.

제거가 하다면 전화 .삭제할 필요가 있는 경우는, 전화 주세요.RemoveAll 배열을 한 하는 반면, 내부 배열은 한 번만 재배치됩니다.Remove 하다Array.Copy모든 요소를 제거할 수 있습니다. RemoveAll훨씬 더 효율적입니다.

목록을 있기 에, 제거하려는 요소의 인덱스를 호출하는 입니다.RemoveAt, 냐냐Remove먼저 목록을 트래버스하여 제거할 요소의 인덱스를 찾지만 인덱스는 이미 알고 있습니다.

그래서 대체적으로, 난 전화할 이유가 없다고 본다.Removefor-loop으로 합니다.또한 가능하면 위의 코드를 사용하여 필요에 따라 목록에서 요소를 스트리밍하여 두 번째 데이터 구조를 만들 필요가 없습니다.

.ToList()를 사용하면 다음 질문에서 설명한 것처럼 목록 복사가 생성됩니다.ToList() -- 새로운 목록을 작성합니까?

ToList()를 사용하면 실제로 복사본을 반복하고 있으므로 원래 목록에서 삭제할 수 있습니다.

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

삭제할 항목을 결정하는 함수가 부작용이 없고 항목을 변환하지 않는 경우(순수한 함수) 단순하고 효율적인(선형 시간) 솔루션은 다음과 같습니다.

list.RemoveAll(condition);

부작용이 있다면 다음과 같은 것을 사용합니다.

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

해시가 양호하다고 가정할 때 이 시간은 아직 선형 시간입니다.그러나 해시셋으로 인해 메모리 사용량이 증가합니다.

가 「」만 되어 있는 는, 「」입니다.IList<T>List<T>는 어떻게 하면특별한 포어치 반복기를 할 수 있을까에 대한 나의 대답을 제안한다.이것은 일반적인 구현으로 인해 선형 런타임입니다.IList<T>다른 많은 응답의 2차 런타임과 비교됩니다.

모든 제거는 사용할 수 있는 조건으로 수행됩니다.

list.RemoveAll(item => item.Value == someValue);
List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));

foreach는 사용할 수 없지만 다음과 같이 항목을 제거할 때 루프 인덱스 변수를 전달 및 관리할 수 있습니다.

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

일반적으로 이러한 모든 기술은 반복되는 수집의 동작에 의존합니다.여기에 표시된 기술은 표준 목록(T)과 함께 사용할 수 있습니다(Foreach 루프 중에 항목을 제거할 수 있는 자체 컬렉션 클래스 및 반복기를 작성할 수 있습니다.

for loops는 이 경우 잘못된 구성입니다.

「」를 사용합니다.while

var numbers = new List<int>(Enumerable.Range(1, 3));

while (numbers.Count > 0)
{
    numbers.RemoveAt(0);
}

, 꼭 써야 , 꼭 써야 한다.for

var numbers = new List<int>(Enumerable.Range(1, 3));

for (; numbers.Count > 0;)
{
    numbers.RemoveAt(0);
}

또는 다음과 같습니다.

public static class Extensions
{

    public static IList<T> Remove<T>(
        this IList<T> numbers,
        Func<T, bool> predicate)
    {
        numbers.ForEachBackwards(predicate, (n, index) => numbers.RemoveAt(index));
        return numbers;
    }

    public static void ForEachBackwards<T>(
        this IList<T> numbers,
        Func<T, bool> predicate,
        Action<T, int> action)
    {
        for (var i = numbers.Count - 1; i >= 0; i--)
        {
            if (predicate(numbers[i]))
            {
                action(numbers[i], i);
            }
        }
    }
}

사용방법:

var numbers = new List<int>(Enumerable.Range(1, 10)).Remove((n) => n > 5);

LINQ에는 ", LINQ"가 있습니다.RemoveAll() 일을 하다

var numbers = new List<int>(Enumerable.Range(1, 10));
numbers.RemoveAll((n) => n > 5);

LINQ를 하는 것이 것 .Where()기존 목록을 변환하는 대신 필터링하여 새 목록을 만듭니다.변은보보 좋좋좋좋좋

var numbers = new List<int>(Enumerable.Range(1, 10))
    .Where((n) => n <= 5)
    .ToList();

「」를 사용합니다.Remove ★★★★★★★★★★★★★★★★★」RemoveAt그 목록을 반복하는 동안 의도적으로 어렵게 만들었다. 왜냐하면 그것은 거의 항상 잘못된 일이기 때문이다.교묘한 속임수로 작동시킬 수 있을지는 모르지만, 매우 느릴 것입니다.전화할 때마다Remove삭제할 요소를 찾으려면 목록 전체를 스캔해야 합니다.RemoveAt후속 요소 1 위치를 왼쪽으로 이동해야 합니다.와 같이, 「 」, 「 」를 사용하는 임의의 .Remove ★★★★★★★★★★★★★★★★★」RemoveAt는 2차 시간 O(n²)필요합니다.

RemoveAll네가 할 수 있으면.그렇지 않으면 다음 패턴에 따라 리스트가 선형 시간 O(n)로 필터링됩니다.

// Create a list to be filtered
IList<int> elements = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Filter the list
int kept = 0;
for (int i = 0; i < elements.Count; i++) {
    // Test whether this is an element that we want to keep.
    if (elements[i] % 3 > 0) {
        // Add it to the list of kept elements.
        elements[kept] = elements[i];
        kept++;
    }
}
// Unfortunately IList has no Resize method. So instead we
// remove the last element of the list until: elements.Count == kept.
while (kept < elements.Count) elements.RemoveAt(elements.Count-1);

보관하고 싶지 않은 요소를 걸러낸 LINQ 쿼리에서 목록을 다시 할당하겠습니다.

list = list.Where(item => ...).ToList();

목록이 매우 많지 않은 한 이 작업을 수행하는 데 큰 문제가 없을 것입니다.

반복하면서 목록에서 항목을 삭제하는 가장 좋은 방법은 사용하는 것입니다.그러나 사람들이 쓰는 주된 우려 사항은 루프 내에서 복잡한 작업을 수행해야 하거나 복잡한 비교 사례가 있다는 것입니다.

해결책은 다음과 같은 표기법을 사용하는 것입니다.

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

술어가 요소의 부울 속성이라고 가정하고, 그것이 참일 경우 요소를 제거해야 합니다.

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

C# 에서는, 삭제하는 것을 마크 해, 새로운 리스트를 작성해 반복하는 방법이 있습니다.

foreach(var item in list.ToList()){if(item.Delete) list.Remove(item);}  

또는 linq를 간단하게 사용할 수도 있습니다.

list.RemoveAll(p=>p.Delete);

그러나 삭제 중 다른 태스크나 스레드가 동일한 목록에 동시에 액세스할 수 있는지, 대신 Concurrent List를 사용할 수 있는지 고려해 볼 필요가 있습니다.

'패턴'이 이런 거였으면 좋겠어요

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

이것은 프로그래머의 뇌에서 진행되는 과정에 코드를 맞출 것이다.

foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

첫 번째 목록에서 완전히 새로운 목록을 작성하기만 하면 됩니다.완전히 새로운 목록을 작성하면 이전 방법보다 성능이 향상되기 때문에 "적당하다"가 아니라 "간단하다"고 말합니다(벤치마킹은 전혀 신경 쓰지 않습니다).저는 일반적으로 이 패턴을 선호합니다.또한 Linq-to-Entities의 한계를 극복하는 데도 도움이 됩니다.

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

이렇게 하면 일반 오래된 For 루프를 사용하여 목록을 거꾸로 순환합니다.컬렉션 크기가 변경되면 앞으로 이동하면 문제가 발생할 수 있지만 뒤로 이동하면 항상 안전합니다.

혹시 도움이 될까 봐 2센트를 덧붙이고 싶습니다만, 같은 문제가 있었습니다만, 반복하는 동안 어레이 리스트에서 여러 요소를 삭제할 필요가 있었습니다.가장 높은 투표율을 기록한 답변은 오류가 발생하여 인덱스가 어레이 리스트의 크기보다 크다는 것을 깨닫기 전까지는 대부분 이 작업을 수행했습니다.여러 요소가 삭제되고 있지만 루프의 인덱스가 이를 추적하지 않았기 때문입니다.간단한 확인으로 해결했습니다.

ArrayList place_holder = new ArrayList();
place_holder.Add("1");
place_holder.Add("2");
place_holder.Add("3");
place_holder.Add("4");

for(int i = place_holder.Count-1; i>= 0; i--){
    if(i>= place_holder.Count){
        i = place_holder.Count-1; 
    }

// some method that removes multiple elements here
}

여기에 언급되지 않은 옵션이 있습니다.

프로젝트의 어딘가에 코드를 조금 추가하는 것이 문제가 되지 않는 경우 목록에 추가 및 확장하여 목록을 역방향으로 반복하는 클래스의 인스턴스를 반환할 수 있습니다.

다음과 같이 사용합니다.

foreach (var elem in list.AsReverse())
{
    //Do stuff with elem
    //list.Remove(elem); //Delete it if you want
}

확장자는 다음과 같습니다.

public static class ReverseListExtension
{
    public static ReverseList<T> AsReverse<T>(this List<T> list) => new ReverseList<T>(list);

    public class ReverseList<T> : IEnumerable
    {
        List<T> list;
        public ReverseList(List<T> list){ this.list = list; }

        public IEnumerator GetEnumerator()
        {
            for (int i = list.Count - 1; i >= 0; i--)
                yield return list[i];
            yield break;
        }
    }
}

이것은 기본적으로 목록입니다.할당되지 않은 리버스().

앞서 언급한 것처럼 요소를 하나씩 삭제해야 하는 단점이 있습니다. 목록이 매우 길면 여기에 있는 옵션 중 일부가 더 좋습니다.하지만 나는 누군가가 목록의 단순함을 원하는 세상이 있다고 생각한다.메모리 오버헤드가 없는 리버스().

반복하고 있는 리스트를 카피합니다.그런 다음 복사본에서 제거하고 원고를 끼워 넣습니다.거꾸로 가는 것은 혼란스럽고 병렬로 루프할 때 잘 작동하지 않습니다.

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});

나는 이렇게 하고 싶다

using System.IO;
using System;
using System.Collections.Generic;

class Author
    {
        public string Firstname;
        public string Lastname;
        public int no;
    }

class Program
{
    private static bool isEven(int i) 
    { 
        return ((i % 2) == 0); 
    } 

    static void Main()
    {    
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", no = 2 },
            new Author{ Firstname = "Fred", Lastname = "Jones", no = 3 },
            new Author{ Firstname = "Brian", Lastname = "Brains", no = 4 },
            new Author{ Firstname = "Billy", Lastname = "TheKid", no = 1 }
        };

        authorsList.RemoveAll(item => isEven(item.no));

        foreach(var auth in authorsList)
        {
            Console.WriteLine(auth.Firstname + " " + auth.Lastname);
        }
    }
}

산출량

Fred Jones
Billy TheKid

나는 내가 주어진 n개의 요소들을 모두th 제거해야 하는 비슷한 상황에 처했다는 것을 알았다.List<T>.

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}

목록에서 항목을 제거하는 비용은 제거할 항목 다음에 나오는 항목 수에 비례합니다.아이템의 전반을 삭제할 수 있는 경우 아이템을 개별적으로 삭제하는 접근방식은 N*N/4 아이템 복사 작업을 수행해야 합니다.이 작업은 리스트가 클 경우 비용이 매우 많이 듭니다.

보다 빠른 접근법은 목록을 스캔하여 제거할 첫 번째 항목(있는 경우)을 찾은 후 그 시점부터 각 항목을 해당 항목이 속한 위치에 복사하는 것입니다.이렇게 하면 R항목을 보유해야 하는 경우 목록의 첫 번째 R항목이 해당 R항목이 되고 삭제가 필요한 모든 항목이 마지막에 됩니다.역순으로 삭제하면 복사하지 않아도 되기 때문에 첫 번째 F를 포함한 R 항목이 N개 남아 있으면 R-F 항목을 복사하고 N-R 횟수를 1개 축소할 필요가 있습니다.모든 선형 시간.

제 접근 방식은 먼저 인덱스 목록을 만드는 것입니다. 이 목록은 삭제되어야 합니다.그런 다음 인덱스를 루프하여 초기 목록에서 항목을 제거합니다.이것은 다음과 같습니다.

var messageList = ...;
// Restrict your list to certain criteria
var customMessageList = messageList.FindAll(m => m.UserId == someId);

if (customMessageList != null && customMessageList.Count > 0)
{
    // Create list with positions in origin list
    List<int> positionList = new List<int>();
    foreach (var message in customMessageList)
    {
        var position = messageList.FindIndex(m => m.MessageId == message.MessageId);
        if (position != -1)
            positionList.Add(position);
    }
    // To be able to remove the items in the origin list, we do it backwards
    // so that the order of indices stays the same
    positionList = positionList.OrderByDescending(p => p).ToList();
    foreach (var position in positionList)
    {
        messageList.RemoveAt(position);
    }
}

제거할 요소를 속성으로 추적하고 프로세스 후 모두 제거합니다.

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = false;
foreach (MyProperty currObj in _Group)
{
    // here it is supposed that you decide the "remove conditions"...
    cond = true; // set true or false...
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);
myList.RemoveAt(i--);

simples;

언급URL : https://stackoverflow.com/questions/1582285/how-to-remove-elements-from-a-generic-list-while-iterating-over-it

반응형