根据对象属性选择对象列表的独占列表

时间:2016-06-15 22:25:06

标签: c# linq

我有一个类的列表,其中包含另一个不同类的列表,例如:

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大小的&#39; jobSteps&#39;以及每个&#39; jobStep&#39;可以有一个N大小的“工作”列表,但是,相同的工作&#39;可以在一个以上的步骤中进行,通常是在一个提升的步骤中。

如何创建“工作步骤”列表,该列表仅包含最后一步的工作&#39;它存在于,换句话说,Max&#39; stepOrder&#39;?

我有以下功能,遍历每个&job;步骤&#39;例如,列表,然后仅选择他们不在以后的“步骤”中的jobID。

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编写此内容,因为我必须在给定时间处理大量数据?

2 个答案:

答案 0 :(得分:1)

如果你真的想要使用内置的LINQ运算符,它将会有点复杂。这将有效:

<div class="cont">
  <div class="imgDetails">
    <p>image selection</p>
    <p>image name</p>
  </div>
  <ul class="imagesHolder">
    <li><img class="high" src="http://placehold.it/150x120/3498db" alt="first image"></li>
    <li><img class="high" src="http://placehold.it/150x120/8e44ad" alt="second image"></li>
    <li><img class="high" src="http://placehold.it/150x120/e74c3c" alt="third image"></li>
  </ul>
</div>

基本上,你想要:

  • 压扁你的清单
  • 按职务ID分组
  • 为每个作业选择第一个作业步骤
  • 按工作步骤分组
  • 最后,'补充'你的清单

在此示例中,我创建了全新的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(); jobs对象,以避免副作用。

如果您要推广自己的扩展方法,我相信您会获得更好的性能。如果我今晚稍后有时间,我将展示一个示例实现。

编辑 - 其他方法

以上方法略有改动,我认为可以给你带来更好的表现。我基本上用保留备忘录的聚合函数替换第一个jobSteps

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,则可能会稍微简化一下。

答案 1 :(得分: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();

我得到列表,按相反的顺序对步骤进行排序。然后我检查工作的ID。我跟踪我遇到的所有ID。如果ID是新的 - 将作业保留在集合中。否则忽略它。将新找到的ID添加到ID集合中。

结果是您的期望:

enter image description here

相关问题