Самосоединение Linq и фильтрация

#c# #linq #self-join

#c# #linq #самосоединение

Вопрос:

У меня есть List<ClaimEvent> состоящий из этого класса:

 public class ClaimEvent
{
    public ClaimEventType ClaimEventClaimEventType { get; set; }
    public DateTime OccurredOn { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
    public string CreatedBy { get; set; }
    public string ModifiedBy { get; set; }
}
  

‘ClaimEventType’ выглядит примерно так…

 public class ClaimEventType
{
    public ClaimEventType()
    {
        Cancels = new List<ClaimEventType>();
        CancelledBy = new List<ClaimEventType>();
    }

    public int ClaimEventTypeId { get; set; }
    public string ClaimEventTypeName { get; set; }
    public List<ClaimEventType> Cancels { get; set; }
    public List<ClaimEventType> CancelledBy { get; set; }
}
  

Cancels перечисляет все типы событий, которые это событие отменяет, когда оно появляется после них в списке, упорядоченном по OccurredOn . CancelledBy является обратным, то есть событие отменяется, если после него появляется одно из CancelledBy событий.

Как я могу запросить список этих объектов, чтобы элементы, которые отменяются другими элементами в списке, не отображались в результатах?

Комментарии:

1. Этот класс не содержит ни OccurredOn , ни какого-либо другого вида даты-времени. Как бы вы объединили их вместе?

2. Я просто добавлял эту часть, извините. Теперь должно быть понятно.

3. являются ли списки Cancels и CancelledBy уже заполненными, когда вы хотите выполнить фильтрацию?

4. @Anthony: да, это так. Я опустил код для краткости

Ответ №1:

Довольно просто, хотя вы, похоже, дублируете усилия, перечисляя как отмененные, так и отмененные с помощью:

 List<ClaimEvent> theList = new List<ClaimEvent>();

theList.RemoveAll(i => (from j in theList
                        where j.ClaimEventClaimEventType.Cancels.Contains(i.ClaimEventClaimEventType) amp;amp;
                        j.OccurredOn > i.OccurredOn
                        select j).Count() > 0);
  

Удалите все элементы из объекта collection, где в коллекции существует другое событие ClaimEvent, которое отменяет событие ClaimEvent типа этого элемента и произошло после этого события claim (т. е. где есть один или более таких элементов).

РЕДАКТИРОВАТЬ: функционально эквивалентный код с более читаемым синтаксисом

Это также может быть выполнено с использованием второго метода делегирования в вызове Exists для поиска любых событий отмены:

 theList.RemoveAll(i =>
    theList.Exists(j =>
        j.ClaimEventClaimEventType.Cancels.Contains(i.ClaimEventClaimEventType) amp;amp;
        j.OccurredOn > i.OccurredOn));
  

Ресурсы

MSDN: Список (из T).Метод removeAll

Комментарии:

1. Я пробовал все виды самосоединений. Я понятия не имел, что существует метод removeAll! Спасибо! Я предоставляю как Cancels, так и CancelsBy, чтобы учитывать разные способы мышления о событиях и о том, как они отменяют друг друга. Это упростит моделирование данных бизнес-пользователям, разрабатывающим рабочие процессы, но, да, мне будет сложнее программировать.

2. О, есть МАССА замечательных методов расширения, которые можно использовать. Я FindAll очень часто злоупотребляю, чтобы помочь мне отфильтровать набор результатов, которые технически можно было бы использовать здесь точно так же, изменив условное обозначение с Count() > 0 на Count = 0 , но синтаксически RemoveAll это больше говорит о том, что вы собираетесь делать.

3. В этом есть ошибка. Мне не нужны отменяющие события, мне нужны отмененные события. Вы должны выбирать i , а не j

4. Вы не пытаетесь выбрать i с помощью этого вызова, а скорее помечаете его для удаления. Этот предикат возвращает только логическое значение, которое гласит «Истина или ложь: существует одно или несколько событий (т. Е. Count() > 0 ), которые отменят меня и приведут к удалению из списка». Я собираюсь отредактировать ответ, добавив ссылку на дополнительную информацию о расширении и использовании предикатов в целом.

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

Ответ №2:

Если я правильно понимаю ваше требование, я думаю, вы могли бы захотеть сделать что-то вроде этого. По сути, он повторяет последовательность и создает HashSet уже существующие типы событий. Для каждого ClaimEvent в последовательности он проверяет ранее существующие типы событий на наличие одного из типов отмены текущего объекта. Если он не находит ни одного, он может выдать текущий объект и добавить его тип в набор.

 public static IEnumerable<ClaimEvent> GetUncancelledEvents(this IEnumerable<ClaimEvent> source)
{   
    // note: override Equals amp; GetHashCode in ClaimEventType**
    HashSet<ClaimEventType> existingEventTypes = new HashSet<ClaimEventType>();

    foreach (var @event in source)
    {
        bool isCancelled = false;
        foreach (var cancellingEvent in @event.ClaimEventClaimEventType.CancelledBy)
        {
            if (existingEventTypes.Contains(cancellingEvent))
            {
                isCancelled = true;
                break;
            }
        }

        if (!isCancelled)
        {
            existingEventTypes.Add(@event.ClaimEventClaimEventType);
            yield return @event;
        }
    }
}
  

 var uncancelledEvents = eventsList.GetUncancelledEvents();