我有一个类的列表,其中包含另一个不同类的列表,例如:
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编写此内容,因为我必须在给定时间处理大量数据?
答案 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>
基本上,你想要:
在此示例中,我创建了全新的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集合中。
结果是您的期望: