【问题标题】:Filter a List of Objects Based on A Different List of Objects with Linq使用 Linq 根据不同的对象列表过滤对象列表
【发布时间】:2011-06-14 04:52:29
【问题描述】:

我目前有两种类型的 List:List(名为 LintMessages)和 List(名为 FilterList)。

PCMessages Properties:
    public string filename { get; set; }
    public int linenumber { get; set; }
    public string messagetype { get; set; }
    public int messagecode { get; set; }
    public string description { get; set; }
    public string evaluated { get; set; }
    public string comment { get; set; }

Filter Properties:
    public bool applied { get; set; }
    public string messagetype { get; set; }
    public int messagecode { get; set; }
    public string evaluated { get; set; }
    public string filename { get; set; }

目标是从符合任何应用过滤器标准的 LintMessages 中排除 PCMessages,并将它们存储在 List FiltLintMessages 中。例如,如果 FilterList 包含两个过滤器:

Filter1 Properties:
    applied = true;
    messagetype = Warning;
Filter2 Properties:
    applied = true;
    messagecode = 12;
    evaluated = "WIP";

然后,我希望从 LintMessages 创建一个列表 FiltLintMessages,其中排除 messagetype 包含“Warning”或 messagecode=12 或评估包含“WIP”的任何条目。

撰写本文时的代码:

FiltLintMessages = (from mess in LintMessages
                    from filt in FilterList
                    where !mess.evaluated.Contains(filt.evaluated)
                         && !mess.filename.Contains(filt.filename) 
                         && mess.messagecode != filt.messagecode
                         && !mess.messagetype.Contains(filt.messagetype)
                    select mess).ToList(); 

任何帮助将不胜感激。

/* 根据 gaearon 建议的解决方案进行了编辑。使用调试器单步执行表明这是唯一与问题相关的代码。正确的项目位于此块的每个列表中,并且在分配给 FiltLintMessages 时,两个单独的过滤器(排除消息类型“警告”和消息代码 = 10,均已应用)仅排除消息代码 = 10 PCMessages,因为它是最后应用的过滤器列表(使用多个过滤器的多个实例进行测试)。但是,该代码适用于 single 应用的过滤器,不包括 messagetype = "Warning" 和 messagecode = 10。上传更多代码将无济于事,并且会使帖子更加陷入困境(程序是变得相当大)。 */

IEnumerable<PCMessage> messages = LintMessages;

foreach (LintMessageFilter filter in FilterList.Where(f => f.applied))
{
    messages = messages.Where(
               m => !m.evaluated.Contains(filter.evaluated)
                       && !m.filename.Contains(filter.filename)
                       && m.messagecode != filter.messagecode
                       && !m.messagetype.Contains(filter.messagetype)
                );
}

FiltLintMessages = messages.ToList(); //FiltLintMessages already initialized

【问题讨论】:

  • 我认为您的messagetypemessagecode 应该可以为空,否则无法判断它们是否已初始化或仅设置为默认值。
  • 编辑了 PCMessages 的默认属性,以便过滤器的默认值永远不会重叠并提供误报。
  • 您应该使用可为空的类型而不是重要的默认值。比如int?MessageType?(如果MessageType是枚举)。然后,在过滤代码中,检查无效性。
  • 我会调查一下,谢谢。
  • @Bandoth:不要写(something == false),而应该写(!something)(“不是什么东西”)。这是 C# 习惯用法,否则很难阅读代码。

标签: c# linq filtering


【解决方案1】:

这会将每个applied 过滤器按顺序应用于序列。

IEnumerable<PCMessages> messages = LintMessages;

foreach (var filterIter in FilterList.Where(f => f.applied)) {
    var filter = filterIter; // capture current filter
    messages = messages.Where (m =>
                   !m.evaluated.Contains (filter.evaluated)
                   && !m.filename.Contains (filter.filename)
                   && m.messagecode != filter.messagecode
                   && !m.messagetype.Contains (filter.messagetype)
    );
}

var FiltLintMessages = messages.ToList ();

【讨论】:

  • 奇怪。使用此方法似乎适用于任何单个应用的过滤器,但应用多个过滤器似乎只使用 FilterList 中最后应用的一个。
  • @Bandoth:好吧,你找到我了。因为Where 仅对其枚举进行评估,显然filter 始终是最后使用的过滤器。您需要在foreach 中捕获当前过滤器并将其用于过滤。
  • @gaearon:非常感谢。效果很好。
  • @Bandoth:如果答案对您有帮助,请不要犹豫,点赞。您可以通过按下它旁边的向上按钮来执行此操作。谢谢。
猜你喜欢
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-07
  • 1970-01-01
相关资源
最近更新 更多