2016-06-15 2 views
0

у меня есть список класса, который содержит еще один список другого класса, например:Выберите эксклюзивный список списка объектов на основе свойств объекта

public class jobs 
{ 
    public int jobID {get;set;} 
    public string jobName {get;set;} 
} 

public class jobSteps 
{ 
    public int stepID {get;set;} 
    public string stepDescription {get;set;} 
    public int stepOrder {get; set;} 
    public List<jobs> jobCollection {get; set;} 
} 

я могу иметь список N по размерам «jobSteps» , и каждый «jobStep» может иметь N-размерный список «заданий», однако одно и то же «задание» может быть более чем на один шаг, обычно в восходящем «stepOrder».

Как создать список «jobSteps», который содержит только задание на последнем «шаге», которое он присутствует, другими словами, «stepOrder» Max?

У меня есть следующая функция, которая повторяется по каждому списку «jobStep», а затем выбирает только идентификаторы jobID, где они не находятся в более позднем «stepOrder», например.

public class myFunctions 
{ 
    public void getJobLatestStep() 
    { 
     // Example Data: 
     List<jobSteps> jobStepCollection = new List<jobSteps> 
     { 
      new jobSteps() 
      { 
       stepID = 1, 
       stepDescription = "Start", 
       stepOrder = 0, 
       jobCollection = new List<jobs>() 
       { 
        new jobs() { jobID = 1, jobName = "Cook food" }, 
        new jobs() { jobID = 2, jobName = "Do laundry" }, 
        new jobs() { jobID = 3, jobName = "Go to work" } 
       } 
      }, 
      new jobSteps() 
      { 
       stepID = 2, 
       stepDescription = "Continue", 
       stepOrder = 1, 
       jobCollection = new List<jobs>() 
       { 
        new jobs() { jobID = 1, jobName = "Cook food" }, 
        new jobs() { jobID = 2, jobName = "Do laundry" } 
       } 
      }, 
      new jobSteps() 
      { 
       stepID = 3, 
       stepDescription = "Finalise", 
       stepOrder = 2, 
       jobCollection = new List<jobs>() 
       { 
        new jobs() { jobID = 2, jobName = "Do laundry" } 
       } 
      } 
     }; 

     List<jobSteps> lastStepOfJob = new List<jobSteps> {}; 
     foreach (jobSteps c in jobStepCollection) 
     { 
      jobSteps currentStep = c; 

      for (int i = jobStepCollection.IndexOf(c); i < jobStepCollection.Count() - 1; i++){ 
       currentStep.jobCollection = currentStep.jobCollection.Where(x => !jobStepCollection[i].jobCollection.Select(z => z.jobID).ToList().Contains(x.jobID)).ToList(); 
      }; 

      lastStepOfJob.Add(currentStep); 
     }; 
    } 

    //The desired result would be: 
    //stepID = 1 
    //stepDescription = 'Start' 
    //stepOrder = 0 
     //jobID = 3 
     //jobName = 'Go to work' 

    //stepID = 2 
    //stepDescription = 'Continue' 
    //stepOrder = 1 
     //jobID = 1 
     //jobName = 'Cook food' 

    //stepID = 3 
    //stepDescription = 'Finalise' 
    //stepOrder = 2 
     //jobID = 2 
     //jobName = 'Do laundry' 
} 

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

ответ

1

Если вы строго хотите использовать встроенные операторы LINQ, это будет выглядеть извращенным. Это будет работать:

List<jobSteps> lastStepOfJob = 
    jobStepCollection 
    .SelectMany(x => x.jobCollection.Select(y => new { JobStep = x, Job = y })) 
    .GroupBy(x => x.Job.jobID) 
    .Select(x => x.OrderByDescending(y => y.JobStep.stepOrder).Select(y => new { JobStep = y.JobStep, Job = y.Job }).First()) 
    .GroupBy(x => x.JobStep.stepOrder) 
    .Select(x => new { JobStep = x.First().JobStep, Jobs = x.Select(y => y.Job) }) 
    .Select(x => new jobSteps() 
    { 
     stepDescription = x.JobStep.stepDescription, 
     stepID = x.JobStep.stepID, 
     stepOrder = x.JobStep.stepOrder, 
     jobCollection = x.Jobs.OrderBy(y => y.jobID).Select(y => new jobs() { jobID = y.jobID, jobName = y.jobName }).ToList() 
    }) 
    .OrderBy(x => x.stepOrder) 
    .ToList(); 

В принципе, вы хотите:

  • расплющить ваш список
  • группу по ID задания
  • выберите первый шаг задания для каждого задания
  • группы по ваш пошаговый заказ
  • , наконец, 'rehydrate' Ваш список

В этом примере я создаю совершенно новые объекты jobs и jobSteps, чтобы избежать побочных эффектов.

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

EDIT - Дополнительный подход

Вот небольшой поворот на таком подходе, который я думаю, мог бы дать вам немного более высокую производительность. Я по существу заменяю первый GroupBy совокупной функцией, которая хранит памятку.

List<jobSteps> lastStepOfJob = 
    jobStepCollection 
    .SelectMany(x => x.jobCollection.Select(y => Tuple.Create(y, x))) 
    .Aggregate(
     new Dictionary<int, Tuple<jobs, jobSteps>>(), 
     (memo, value) => 
     { 
      if (memo.ContainsKey(value.Item1.jobID)) 
      { 
       if (memo[value.Item1.jobID].Item2.stepOrder < value.Item2.stepOrder) 
       { 
        memo[value.Item1.jobID] = value; 
       } 
      } 
      else 
      { 
       memo.Add(value.Item1.jobID, value); 
      } 
      return memo; 
     }) 
    .Select(x => new { Job = x.Value.Item1, JobStep = x.Value.Item2 }) 
    .GroupBy(x => x.JobStep.stepOrder) 
    .Select(x => new { JobStep = x.First().JobStep, Jobs = x.Select(y => y.Job) }) 
    .Select(x => new jobSteps() 
    { 
     stepDescription = x.JobStep.stepDescription, 
     stepID = x.JobStep.stepID, 
     stepOrder = x.JobStep.stepOrder, 
     jobCollection = x.Jobs.OrderBy(y => y.jobID).Select(y => new jobs() { jobID = y.jobID, jobName = y.jobName }).ToList() 
    }) 
    .OrderBy(x => x.stepOrder) 
    .ToList(); 

Стратегия по существу та же стратегия, что и мое первое решение. Я знаю, это выглядит немного пугающе. Вероятно, это может быть немного упрощено, если вы переопределите метод GetHashCode для типов jobs и jobSteps или внедрили пользовательские настройки IEqualityComparer.

+0

Спасибо за редактирование. Это был образец вопроса, который я взял из своего фактического кода (который я не могу опубликовать по причинам интеллектуальной собственности). Пожалуйста, пример очень ценится, так как производительность будет значительным фактором, так как мне нужно будет обрабатывать тысячи записей в день и хранить их. (И я не слишком опытен с Linq, если честно). –

1

Использование коллекции образцов я пришел к этому решению:

var jobIds = new List<int>(); 
var results = jobStepCollection 
    .OrderByDescending(x => x.stepOrder) 
    .Select(x => 
    { 
     var localJobIds = x.jobCollection.Select(y => y.jobID); 
     var newIds = localJobIds.Where(y => !jobIds.Contains(y)); 
     var newJobs = x.jobCollection.Where(y => newIds.Contains(y.jobID)); 

     x.jobCollection = newJobs.ToList(); 
     jobIds.AddRange(newIds); 
     return x; 
    }) 
    .OrderBy(x => x.stepOrder) 
    .ToList(); 

Я получаю список, сортировать в обратном порядке. Затем я проверяю идентификатор задания. Я отслеживаю все идентификаторы, с которыми я столкнулся. Если идентификатор является новым, сохраните задание в коллекции. В противном случае игнорируйте его. Добавьте новые идентификаторы в коллекцию идентификаторов.

В результате того, что вы ожидали:

enter image description here

+0

Благодарим вас за ответ. В настоящее время я использую тесты на обоих подходах, чтобы увидеть, какие из них смогут обрабатывать больший объемный объем данных. Если я могу спросить, какой инструмент вы использовали для визуального отображения содержимого списка? –

+0

Это LinqPad. Если вы создадите коллекцию в консоли, вы получите это представление: 'Console.WriteLine (results);'. – PiotrWolkowski

Смежные вопросы