C# Фильтровать Элементы В Списке По Нескольким Критериям
Во-первых, какова моя ситуация здесь...
- У моего объекта SomeObject есть свойство
string Status, которое меня интересует для этого сценария. - у меня есть метод под названием
FilterObjects, который возвращаетList<SomeObject>
- метод принимает аргумент, такой же, как и его возвращаемый тип,
List<SomeObject>
- должен фильтровать в соответствии со следующими случаями, описанными ниже, и возвращать список объектов.
- В
List<SomeObject>я посылаю в качестве аргумента, чтобы мой метод гарантированно был в порядке (через их ID и тип).
Свойство Status может содержать значения" открыто"," закрыто"," готово " в точности.
Метод
Эти случаи (все они относятся к упомянутому мною свойству string Status):
- Если какой-либо элемент в списке содержит
Status = "Finished";, то исключите все остальные элементы, которые были в исходном списке, и верните только тот объект, который имеет статус "готово".
Если какой-либо элемент не содержит
Status = Finished, но содержит "закрыто", мне нужно проверить, есть ли какой-либо другой элемент это имеет значение "открыто" после того, как "закрыто". Вы можете думать об этом как о "задаче, которую можно закрыть, но можно снова открыть. Но как только он закончен, его уже нельзя будет снова открыть".
Если он содержит " закрытый "и не имеет никакого" открытого " после этого элемента, я буду игнорировать все элементы перед закрытым и возвращать только закрытый объект. Если он содержит "OPEN" после любого закрытого, мне нужно вернуть что-либо после этого закрытого, исключив себя.
Я тоже пытался объяснить то же самое с моими удивительными навыками 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>();
}
Короче говоря, каков был бы наилучший и наиболее эффективный способ применить всю эту логику в этом единственном методе? Честно говоря, я не мог пойти дальше первого случая, который я уже реализовал, который является законченным. Может ли все это быть сделано с помощью магии Линка?
Гарантируется, что я получу упорядоченный список И я никогда не получу предметов больше пары сотен, так что коллекция никогда не будет массовой.
Заранее большое спасибо за помощь.
6 ответов:
Вы можете попробовать что-то вроде этого:
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; }
Я действительно не стал бы использовать 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; }EDIT: слегка изменил последний бит кода, чтобы он не вызывал исключение, если список объектов пуст
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); }
Я сделал это так:
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; }); }
Вы можете написать такой метод. Это как минимум, что вам придется добавить проверку null и обработку исключений.
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; }
Что-то вроде этого:
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; }

Comments