Как удалить элементы из общего списка при переборах по нему?

Necromancing. Если используется поставщик LINQ, основанный на базе данных, может быть записано значительно более читаемое левое внешнее соединение:

from maintable in Repo.T_Whatever 
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()

Если вы опустите DefaultIfEmpty(), у вас будет внутреннее соединение.

Возьмите принятый ответ:

  from c in categories
    join p in products on c equals p.Category into ps
    from p in ps.DefaultIfEmpty()

Этот синтаксис очень сбивает с толку, и неясно, как это работает, когда вы хотите уйти, чтобы присоединиться к таблицам MULTIPLE.

Примечание. Следует отметить, что from alias in Repo.whatever.Where(condition).DefaultIfEmpty() является таким же, как внешний-apply / left-join-lateral, который любой (не отсталый) оптимизатор базы данных отлично способен перевести в левое соединение , до тех пор, пока вы не вводите значения строк (например, фактическое внешнее применение). Не делайте этого в Linq-2-Objects (потому что при использовании Linq-to-Objects нет DB-оптимизатора).

Подробный пример

var query2 = (
    from users in Repo.T_User
    from mappings in Repo.T_User_Group
         .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
         .DefaultIfEmpty() // <== makes join left join
    from groups in Repo.T_Group
         .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
         .DefaultIfEmpty() // <== makes join left join

    // where users.USR_Name.Contains(keyword)
    // || mappings.USRGRP_USR.Equals(666)  
    // || mappings.USRGRP_USR == 666 
    // || groups.Name.Contains(keyword)

    select new
    {
         UserId = users.USR_ID
        ,UserName = users.USR_User
        ,UserGroupId = groups.ID
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

При использовании с LINQ 2 SQL он хорошо переведёт на следующий очень читаемый SQL-запрос:

SELECT 
     users.USR_ID AS UserId 
    ,users.USR_User AS UserName 
    ,groups.ID AS UserGroupId 
    ,groups.Name AS GroupName 
FROM T_User AS users

LEFT JOIN T_User_Group AS mappings
   ON mappings.USRGRP_USR = users.USR_ID

LEFT JOIN T_Group AS groups
    ON groups.GRP_ID == mappings.USRGRP_GRP

Редактировать:

См. Также & quot; Конвертировать запрос SQL Server в запрос Linq & quot; для более сложного примера.

Кроме того, если вы делаете это в Linq-2-Objects (вместо Linq-2-SQL), вы должны сделать это старомодно (потому что LINQ to SQL правильно это переводит для присоединения операции, но над объектами этот метод заставляет полностью сканировать и не использует поиск по индексу, почему ...):

    var query2 = (
    from users in Repo.T_Benutzer
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
    from mappings in tmpMapp.DefaultIfEmpty()
    from groups in tmpGroups.DefaultIfEmpty()
    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);

408
задан Vikrant 16 January 2017 в 12:23
поделиться

8 ответов

Итерируйте свой список в обратном порядке с помощью цикла 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));
680
ответ дан 22 November 2019 в 23:26
поделиться

Проследите элементы, которые будут удалены со свойством и удалите их всех после процесса.

using System.Linq;

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

bool cond = true;
foreach (MyProperty currObj in _Group)
{
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);
0
ответ дан Roberto Mutti 4 November 2019 в 09:07
поделиться

Просто требуемый для добавления моих 2 центов к этому в случае, если это помогает любому я имел подобную проблему, но должен был удалить несколько элементов из списка массива, в то время как это выполнялось с помощью итераций. самый высокий ответ upvoted сделал это для меня по большей части, пока я не столкнулся с ошибками и понял, что индекс был больше, чем размер списка массива в некоторых случаях, потому что несколько элементов удалялись, но индекс цикла не отслеживал это. Я зафиксировал это с простой проверкой:

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
}
0
ответ дан 22 November 2019 в 23:26
поделиться

Я сделал бы как это

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
1
ответ дан 22 November 2019 в 23:26
поделиться

Я бы переназначил список из запроса LINQ, который отфильтровывал элементы, которые вы не хотели оставлять .

3
ответ дан 22 November 2019 в 23:26
поделиться

Использование ToArray () в общем списке позволяет вам удалить (элемент) в вашем общем списке:

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }
21
ответ дан 22 November 2019 в 23:26
поделиться

Выберите элементы, которые вы делать нужно вместо того, чтобы пытаться удалить элементы, которые не нужны . Это намного проще (и, как правило, более эффективно), чем удаление элементов.

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

Я хотел опубликовать это как комментарий в ответ на комментарий, оставленный Майклом Диллоном ниже, но он слишком длинный и, вероятно, будет полезен в моем ответе:

Лично я бы никогда не удалял элементы один за другим. -one, если требуется удаление, вызовите RemoveAll , который принимает предикат и только один раз переупорядочивает внутренний массив, тогда как Remove выполняет операцию Array.Copy . для каждого удаляемого вами элемента. RemoveAll намного эффективнее.

И когда вы перебираете список в обратном направлении, у вас уже есть индекс элемента, который вы хотите удалить, поэтому было бы намного эффективнее вызвать RemoveAt , потому что Remove сначала выполняет обход списка, чтобы найти индекс элемента, который вы пытаетесь удалить, но вы уже знаете этот индекс.

В общем, я не вижу причин для вызова Remove в цикле for. И в идеале, если это вообще возможно, используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, чтобы не создавать вторую структуру данных вообще.

поэтому было бы гораздо эффективнее вызвать RemoveAt , потому что Remove сначала выполняет обход списка, чтобы найти индекс элемента, который вы пытаетесь удалить, но вы уже знаете этот индекс.

В общем, я не вижу причин для вызова Remove в цикле for. И в идеале, если это вообще возможно, используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, чтобы не создавать вторую структуру данных вообще.

поэтому было бы гораздо эффективнее вызвать RemoveAt , потому что Remove сначала выполняет обход списка, чтобы найти индекс элемента, который вы пытаетесь удалить, но вы уже знаете этот индекс.

В общем, я не вижу причин для вызова Remove в цикле for. И в идеале, если это вообще возможно, используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, чтобы не создавать вторую структуру данных вообще.

используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, поэтому вторую структуру данных создавать не нужно.

используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, поэтому вторую структуру данных создавать не нужно.

21
ответ дан 22 November 2019 в 23:26
поделиться

Простое и понятное решение:

Используйте стандартный цикл for, выполняющий в обратном направлении в вашей коллекции и RemoveAt (i) , чтобы удалить элементы .

83
ответ дан 22 November 2019 в 23:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: