C# Фильтровать Элементы В Списке По Нескольким Критериям



Во-первых, какова моя ситуация здесь...

  • У моего объекта SomeObject есть свойство string Status, которое меня интересует для этого сценария.
  • Свойство Status может содержать значения" открыто"," закрыто"," готово " в точности.
  • у меня есть метод под названием FilterObjects, который возвращает List<SomeObject>
  • метод принимает аргумент, такой же, как и его возвращаемый тип, List<SomeObject>
  • Метод
  • должен фильтровать в соответствии со следующими случаями, описанными ниже, и возвращать список объектов.
  • В List<SomeObject> я посылаю в качестве аргумента, чтобы мой метод гарантированно был в порядке (через их ID и тип).

Эти случаи (все они относятся к упомянутому мною свойству string Status):

  1. Если какой-либо элемент в списке содержит Status = "Finished";, то исключите все остальные элементы, которые были в исходном списке, и верните только тот объект, который имеет статус "готово".
  2. Если какой-либо элемент не содержит 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>();
}
Короче говоря, каков был бы наилучший и наиболее эффективный способ применить всю эту логику в этом единственном методе? Честно говоря, я не мог пойти дальше первого случая, который я уже реализовал, который является законченным. Может ли все это быть сделано с помощью магии Линка?

Гарантируется, что я получу упорядоченный список И я никогда не получу предметов больше пары сотен, так что коллекция никогда не будет массовой.

Заранее большое спасибо за помощь.

213   6  

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;
     }
    Ничего не найдено.

Добавить ответ:
Отменить.