首先,我的情况是......
string Status
,我对这种情况感兴趣。FilterObjects
的方法,它返回List<SomeObject>
List<SomeObject>
List<SomeObject>
作为我的方法的参数,保证按顺序排列(通过他们的ID和类型)。案例(都与我提到的string Status
属性相关):
Status = "Finished";
,则删除原始列表中的所有其他元素,并仅返回具有“已完成”状态的对象。如果任何项目不包含Status = Finished
但包含“已关闭”,我需要检查是否有任何其他项目之后具有“打开”值“关闭”一个。您可以将此视为“任务可以关闭,但可以重新打开。但一旦完成,就无法重新打开”。
如果它包含“CLOSED”并且在该项之后没有任何“OPEN”,我将忽略CLOSED之前的所有项目并仅返回CLOSED对象。如果它在任何关闭之后包含“OPEN”,我需要在CLOSED之后返回任何内容,排除它自己。
我也尝试用我令人敬畏的MS Paint技巧解释同样的事情。
对象本身并不是一个问题,但我的方法是这样的:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
var objects = objectList;
var returnList = new List<SomeObject>();
foreach (var obj in objects)
{
if (obj.Status == "Finished")
{
returnList.Add(obj);
return returnList;
}
}
return new List<SomeObject>();
}
长话短说,在这种方法中应用所有这些逻辑的最佳和最有效的方法是什么?老实说,我不能比我已经实施的第一个案例更进一步,这是完成的。可以用一些LINQ魔法来完成这一切吗?
保证我收到一份有序的清单,而且我永远不会收到超过几百件的物品,所以收藏品永远不会很大。
非常感谢您的帮助。
答案 0 :(得分:4)
你可以尝试这样的事情:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
SomeObject finished = objectList.FirstOrDefault(o => o.Status.Equals("Finished"));
if (finished != null) { return new List<SomeObject> { finished }; }
List<SomeObject> closed = objectList.SkipWhile(o => !o.Status.Equals("Closed")).ToList();
if (closed.Count == 1) { return closed; }
if (closed.Count > 1) { return closed.Skip(1).ToList(); }
// if you need a new list object than return new List<SomeObject>(objectList);
return objectList;
}
答案 1 :(得分:3)
我真的不打算使用Linq,因为你要么创建一个过于复杂的管理指令,要么需要多次循环迭代。我会选择这样的东西:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int lastClosed = -1;
for (int i = 0; i < objectList.Count; i++)
{
if (objectList[i].Status == "Closed")
lastClosed = i;
else if (objectList[i].Status == "Finished")
return new List<SomeObject>() { objectList[i] };
}
if (lastClosed > -1)
if (lastClosed == objectList.Count - 1)
return new List<SomeObject>() { objectList[lastClosed] };
else
return objectList.Skip(lastClosed + 1).ToList();
else
return objectList;
}
编辑:稍微更改了代码的最后一位,以便在objectList为空时不会触发异常
答案 2 :(得分:2)
对于需要根据序列的上一个/下一个元素应用逻辑的场景,LINQ不太适合且效率低下。
应用逻辑的最佳方法是使用单个循环并跟踪Closed
状态和发生状态更改的位置。最后,如果最后一个状态为Closed
,则返回该位置的单个元素,否则返回从该位置开始的范围。
static List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int pos = 0;
bool closed = false;
for (int i = 0; i < objectList.Count; i++)
{
var item = objectList[i];
if (item.Status == "Finished")
return new List<SomeObject> { item };
if (item.Status == (closed ? "Opened" : "Closed"))
{
pos = i;
closed = !closed;
}
}
return objectList.GetRange(pos, closed ? 1 : objectList.Count - pos);
}
答案 3 :(得分:1)
我是这样做的:
public static IEnumerable<SomeObject> convert(this IEnumerable<SomeObject> input)
{
var finished = input.FirstOrDefault(x => x.Status == "Finished");
if (finished != null)
{
return new List<SomeObject> {finished};
}
return input.Aggregate(new List<SomeObject>(), (a, b) =>
{
if (!a.Any())
{
a.Add(b);
}
else if (b.Status == "Open")
{
if (a.Last().Status == "Closed")
{
a.Remove(a.Last());
}
a.Add(b);
}
else if (b.Status == "Closed")
{
a = new List<SomeObject> {b};
}
return a;
});
}
答案 4 :(得分:0)
你可以写一个像这样的方法。这是最低限度,您必须添加空检查和异常处理。
public List<SomeCls> GetResult(List<SomeCls> lstData)
{
List<SomeCls> lstResult;
if(lstData.Any(x=>x.Status=="Finished"))
{
lstResult = lstData.Where(x=>x.Status=="Finished").ToList();
}
else if(lstData.Any(x=>x.Status=="Closed"))
{
// Here assuming that there is only one Closed in whole list
int index = lstData.FindIndex(0,lstData.Count(),x=>x.Status=="Closed");
lstResult = lstData.GetRange(index,lstData.Count()-index);
if(lstResult.Count()!=1) // check if it contains Open.
{
lstResult = lstResult.Where(x=>x.Status=="Open").ToList();
}
}
else // Only Open
{
lstResult = lstData;
}
return lstResult;
}
答案 5 :(得分:0)
类似的东西:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
if (objectList.Where(x => x.Status == "Finished").Any())
{
return objectList.Where(x => x.Status == "Finished").ToList();
}
else if (objectList.Where(x => x.Status == "Closed").Any())
{
if (objectList.FindIndex(x => x.Status == "Closed") == objectList.Count() - 1)
{
return objectList.Where(x => x.Status == "Closed").ToList();
}
else
{
return objectList.GetRange(objectList.FindIndex(x => x.Status == "Closed") + 1, objectList.Count() - (objectList.FindIndex(x => x.Status == "Closed") + 1));
}
}
return objectList;
}