C#根据多个标准过滤列表中的项目

时间:2017-01-27 08:16:42

标签: c# .net algorithm linq list

首先,我的情况是......

  • 我的SomeObject有一个属性string Status,我对这种情况感兴趣。
  • Status属性可以包含“Open”,“Closed”,“Finished”值。
  • 我有一个名为FilterObjects的方法,它返回List<SomeObject>
  • Method接受与其返回类型List<SomeObject>
  • 相同的参数
  • 方法应根据下面解释的以下情况进行过滤并返回对象列表。
  • 我发送的List<SomeObject>作为我的方法的参数,保证按顺序排列(通过他们的ID和类型)。

案例(都与我提到的string Status属性相关):

  1. 如果列表中的任何项目包含Status = "Finished";,则删除原始列表中的所有其他元素,并仅返回具有“已完成”状态的对象。
  2. 如果任何项目包含Status = Finished但包含“已关闭”,我需要检查是否有任何其他项目之后具有“打开”值“关闭”一个。您可以将此视为“任务可以关闭,但可以重新打开。但一旦完成,就无法重新打开”。

    如果它包含“CLOSED”并且在该项之后没有任何“OPEN”,我将忽略CLOSED之前的所有项目并仅返回CLOSED对象。如果它在任何关闭之后包含“OPEN”,我需要在CLOSED之后返回任何内容,排除它自己。

  3. 我也尝试用我令人敬畏的MS Paint技巧解释同样的事情。

    enter image description here

    对象本身并不是一个问题,但我的方法是这样的:

    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魔法来完成这一切吗?

    保证我收到一份有序的清单,而且我永远不会收到超过几百件的物品,所以收藏品永远不会很大。

    非常感谢您的帮助。

6 个答案:

答案 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;
     }