Расширяемое реляционное деление в LINQ

В этом примере класса IcdPatient представляет отношение "многие ко многим" между таблицей пациента (не показанной в этом примере) и таблицей поиска Icd,

public class IcdPatient
    {
        public int PatientId { get; set; }
        public int ConditionCode { get; set; }

        public static List<IcdPatient> GetIcdPatientList()
        {
            return new List<IcdPatient>()
            {
                new IcdPatient { PatientId = 100, ConditionCode = 111 },
                new IcdPatient { PatientId = 100, ConditionCode = 222 },
                new IcdPatient { PatientId = 200, ConditionCode = 111 },
                new IcdPatient { PatientId = 200, ConditionCode = 222 },
                new IcdPatient { PatientId = 3, ConditionCode = 222 },
            };
        }
    }
    public class Icd
    {
        public int ConditionCode { get; set; }
        public string ConditionName { get; set; }

        public static List<Icd> GetIcdList()
        {
            return new List<Icd>()
            {
                new Icd() { ConditionCode =111, ConditionName ="Condition 1"},
                new Icd() { ConditionCode =222, ConditionName ="Condition 2"},
            };
        }
    }

Я хотел бы, чтобы пользователь мог ввести столько условий, сколько он хочет, и получить обратно объект LINQ, который сообщает им, сколько PatientIdудовлетворить этот запрос. Я придумал:

List<string> stringFilteredList = new List<string> { "Condition 1", "Condition 2" };
            List<int> filteringList = new List<int> { 111,222 };
            var manyToMany = IcdPatient.GetIcdPatientList();
            var icdList = Icd.GetIcdList();
            /*Working method without joining on the lookup table*/
            var grouped = from m in manyToMany
                          group m by m.PatientId into g
                          where g.Count() == filteringList.Distinct().Count()
                          select new
                          {
                              PatientId = g.Key,
                              Count = g.Count()
                          };
            /*End*/
            foreach (var item in grouped)
            {
                Console.WriteLine(item.PatientId);
            }

Скажем так IcdPatient имеет составной первичный ключ в обоих полях, поэтому мы знаем, что каждая строка уникальна. Если мы найдем различное количество записей в filteringList и рассчитывать на количество раз PatientId появляется, это означает, что мы нашли всех людей, у которых есть все условия. Поскольку коды могут быть эзотерическими, я хотел бы сделать что-то вроде позволить пользовательской таблице в ConditionName введите Icd и выполните ту же операцию. Я не использовал LINQ таким образом, и я собрал:

 List<int> filteringList = new List<int> { 111,222 };
            List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };
            filteringList.Distinct();
            var manyToMany = IcdPatient.GetIcdPatientList();
            var icdList = Icd.GetIcdList();
            /*Working method without joining on the lookup table*/
            var grouped = from m in manyToMany
                          join i in icdList on 
                          m.ConditionCode equals i.ConditionCode
                          //group m by m.PatientId into g
                          group new {m,i} by new { m.ConditionCode }into g
                          where g.Count() == filteringList.Distinct().Count()
                          select new
                          {

                              Condition = g.Key.ConditionCode

                          };
            /*End*/

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

2 ответа

Вам не нужно ничего группировать в этом случае, просто используйте соединение и содержит:

List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };

var patients = 
    from icd in Icd.GetIcdList()
    join patient in IcdPatient.GetIcdPatientList() on icd.ConditionCode equals patient.ConditionCode
    where stringFilteredList.Contains(icd.ConditionName)
    select patient.PatientId;

Допустим, у IcdPatient есть составной первичный ключ в обоих полях, поэтому мы знаем, что каждая строка уникальна. Если мы найдем различное количество записей в filteringList и посчитаем количество раз, которое показывает PatientId, это означает, что мы нашли всех людей, у которых есть все условия. Поскольку коды могут быть эзотерическими, я хотел бы сделать что-то вроде позволить пользовательской таблице в ConditionName типа Icd и выполнить ту же операцию.

Я полагаю, вы спрашиваете:

Приведен список ConditionCodes, вернуть список PatientIdгде каждый пациент имеет все условия в списке.

В этом случае проще всего сгруппировать таблицу IcdPatients по Id, чтобы мы могли определить каждое состояние пациента, просмотрев один раз. Затем мы проверяем, что каждый ConditionCode мы ищем в группе. В коде это выглядит так:

var result = IcdPatient.GetIcdPatientList()
                    // group up all the objects with the same PatientId
                    .GroupBy(patient => patient.PatientId)
                    // gather the information we care about into a single object of type {int, List<int>}
                    .Select(patients => new {Id = patients.Key,
                                            Conditions = patients.Select(p => p.ConditionCode)})
                    // get rid of the patients without every condition
                    .Where(conditionsByPatient =>
                            conditionsByPatient.Conditions.All(condition => filteringList.Contains(condition)))
                    .Select(conditionsByPatient => conditionsByPatient.Id);

В формате запроса это выглядит так:

var groupedInfo = from patient in IcdPatient.GetIcdPatientList()
                  group patient by patient.PatientId
                    into patients
                    select new { Id = patients.Key,
                                 Conditions = patients.Select(patient => patient.ConditionCode) };
var resultAlt = from g in groupedInfo
                where g.Conditions.All(condition => filteringList.Contains(condition))
                select g.Id;

Изменить: Если вы также хотите, чтобы ваш пользователь указал ConditionName а не ConditionId затем просто конвертировать из одного в другой, сохраняя результат в filteringList, вот так:

var conditionNames = // some list of names from the user
var filteringList = Icd.GetIcdList().Where(icd => conditionNames.Contains(icd.ConditionName))
                                    .Select(icd => icd.ConditionCode);
Другие вопросы по тегам