使用 Linq 根据条件过滤对象列表

Posted

技术标签:

【中文标题】使用 Linq 根据条件过滤对象列表【英文标题】:Using Linq to Filter a List of Objects based on a Condition 【发布时间】:2021-12-05 09:49:52 【问题描述】:

我正在解决一个问题,即由于区域名称条件,我想从列表List<RegionData> 中删除不需要的对象。 作为条目输入,我收到var data,其中包含List<CasesDto>。一项CasesDto 对应于某个特定日期收到的所有基于地区的数据。

这是我用作示例的两个类:

public class CasesDto
    
    public DateTime Date get; set;

    public List<RegionData> Region get; set;

public class RegionData

    public string RegionName get; set;

    public int DailyActiveCases get; set;

    public int DeceasedToDate get; set;

这是当前的输出示例(为了便于查看,我只包含了两个日期(= 两个 CasesDto)),其中 region 没有 被考虑在内:

[
    
        "date": "2021-05-22T00:00:00",
        "region": [
            
                "regionName": "ce",
                "dailyActiveCases": 615,
                "deceasedToDate": 568
            ,
            
                "regionName": "kk",
                "dailyActiveCases": 170,
                "deceasedToDate": 197
            ,
            
                "regionName": "kp",
                "dailyActiveCases": 278,
                "deceasedToDate": 166
            
        ]
    ,
    
        "date": "2021-05-23T00:00:00",
        "region": [
            
                "regionName": "ce",
                "dailyActiveCases": 613,
                "deceasedToDate": 570
            ,
            
                "regionName": "kk",
                "dailyActiveCases": 167,
                "deceasedToDate": 197
            ,
            
                "regionName": "kp",
                "dailyActiveCases": 277,
                "deceasedToDate": 166
            
        ]
    
]

如果应用 regionName="ce",这是我想要的输出:

[
    
        "date": "2021-05-22T00:00:00",
        "region": [
            
                "regionName": "ce",
                "dailyActiveCases": 615,
                "deceasedToDate": 568
            
        ]
    ,
    
        "date": "2021-05-23T00:00:00",
        "region": [
            
                "regionName": "ce",
                "dailyActiveCases": 613,
                "deceasedToDate": 570
            
        ]
    
]

我想用 lambda 函数解决这个问题。

我的尝试:

data = data.ForEach(x =&gt; x.Region.Where(t =&gt; t.RegionName == region)).ToList();

但是,我收到的返回类型不匹配。如何访问 RegionData 模型并仅选择我想要的区域项(基于区域标识符)?

【问题讨论】:

var filteredData = data.Where(t => t.Region.RegionName == region); var filtered = data.Where(x =&gt; x.Region.Any(t =&gt; t.RegionName == region)); 这不起作用,因为 t.Region 是一个 List。它找不到 RegionName。它可以与 t.Region[i].RegionName 一起使用,但是没有索引“i”可以帮助我们解决这个问题。 @JochemVanHespen 您的示例已编译,但它不起作用。它返回与输入相同的输出。它没有过滤。 @JochemVanHespen 非常感谢,您在 sn-p 中的回答非常完美。你能发布你的答案以便我批准吗? 【参考方案1】:
    var cases = new List<CasesDto>
        new CasesDto
            Date = DateTime.Now,
            Region = new List<RegionData> 
                new RegionData
                    RegionName = "CE"
                ,
                new RegionData
                    RegionName = "DE"
                
            
        ,
        new CasesDto
            Date = DateTime.Now.AddDays(10),
            Region = new List<RegionData>              
                new RegionData
                    RegionName = "DE"
                
            
        
    ;
    
    
    var filteredCases = new List<CasesDto>();
    var regionName = "CE";
    cases.ForEach(c => 
        if(c.Region.Any(x => x.RegionName == regionName))
            c.Region = c.Region.Where(x => x.RegionName == regionName).ToList();
            filteredCases.Add(c);
        
    );

为了节省我一些时间,稍微简化了一些。使用 ForEach 我们枚举列表并检查我们是否有一个具有给定区域名称的区域。如果是这样,我们过滤当前列表以仅包含正确的区域并将此结果附加到新列表中。

【讨论】:

【参考方案2】:

所以你有一个CasesDtos(复数名词)的可枚举序列,其中每个CasesDto(单数名词)至少有DateRegion的属性。

区域是RegionDatas 的可枚举序列。每个 RegionData 都有几个属性,其中一个属性RegionName

我认为您已经描述了以下要求:

要求:给定regionName 的字符串值和CasesDtos 的序列,给我每个CasesDtoDateRegionData,其中至少有一个RegionData 的值为属性RegionName 等于regionName

如果 CasesDto 有两个具有正确 RegionName 的 RegionData,你没有告诉你想要什么:

string regionName = "CA";
var casesDto = new CasesDto

     Date = ...
     Region = new List<RegionData>()
     
          new RegionData()
          
              RegionName = "CA",
              ...
          ,
          new RegionData()
          
              RegionName = "CA",
              ...
          
    

casesDto 有两个具有匹配 RegionName 的 RegionData。您希望最终结果是什么:只有日期和 RegionData 之一?哪一个?还是您想要所有具有匹配 RegionName 的 RegionData?

不管怎样,解决办法是:

string regionName = "CA";
IEnumerable<CasesDto> casesDtos = ...

// from every CasesDto, make one object, containing the Date, and a property 
// Region, which contains only those RegionDatas that have a value for property 
// RegionName that equals regionName
var result = casesDtos.Select(casesDto => new

    Date = casesDto.Date,

    // keep only those Regions that have a matching RegionName:
    Region = casesDto.Region
        .Where(region => region.RegionName == regionName)
        .ToList(),


// From this selection, keep only those object that have at least one Region:
.Where(casesDto => casesDto.Region.Any());
             

简而言之:对于 caseDtos 中的每个 CasesDto,创建一个具有两个属性的新对象:CasesDto 的日期,以及一个包含此 CasesDto 的 RegionDatas 的属性 Region,该 RegionDatas 具有匹配的 RegionName。从这个 Select 结果中,只保留那些在列表 Region 中至少有一个元素的对象。

【讨论】:

您的回答也提供了正确的结果!谢谢!我根据自己的需要做了一些细微的调整,但逻辑是完美的。我在 var result 变量中放入了 new CasesDto 而不仅仅是 new,所以我绕过了不匹配。

以上是关于使用 Linq 根据条件过滤对象列表的主要内容,如果未能解决你的问题,请参考以下文章

如何过滤复杂对象的列表,以便如果两个具有字段值,我会根据条件删除一个

如何使用 Linq 根据另一个列表过滤列表?

如何根据多个条件过滤列表?

如何使用条件过滤字典值列表

使用多个条件过滤对象,包括比较两个对象字段

根据用户选择的条件过滤列表