#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));
Ресурсы
Комментарии:
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();