2015-12-10 63 views
2

我有试图执行的GroupBy一个简单的LINQ查询,其中在报表中的项目之一是List<string>.LINQ的GroupBy列表<>

var viewModel = reports 
     .GroupBy(c => new { c.Id, c.PetList }) 
     .Select(g => new ArmReportModel 
     { 
      PetList = g.Key.PetList, 
      Pets = g.Count() 
     }); 

在此之前的说法,我执行我的EF库方法最终调用一个方法来创建上面的PetList。

如果我从GroupBy()中删除PetList,它按预期工作。有什么我必须做的,以便通过List<string>类型进行分组?

+1

你怎么组由一个列表?如果你有一个为两个宠物列表定义“平等”的类,那么你可以将它传递给'GroupBy' - 否则默认使用引用相等。 –

+0

如此类似于自定义IComparable? – McArthey

+1

是 - 创建一个实现['IEqualityComparer '](https://msdn.microsoft.com/en-us/library/ms132151(v = vs.110).aspx)的类并将其传递给'GroupBy'。请确保遵循[实现'GetHashCode']的指导原则(https://blogs.msdn.microsoft.com/ericlippert/2011/02/28/guidelines-and-rules-for-gethashcode/)“。 –

回答

3

我认为Id是一个标识符,因此具有相同Id的任何两个c实际上是相同的并且具有相同的PetList。因此,我们可以GroupBy只是Id,并获得PetList另一种方式:

var viewModel = reports 
    .GroupBy(c => c.Id) 
    .Select(g => new ArmReportModel 
    { 
    PetList = g.First().PetList, // Might need FirstOrDefault() with some providers 
    Pets = g.Count() 
    }); 

除非那个,我想先确保我可以用一个IEqualityComparer<T>GroupBy。如果提供者允许的话,那么没有问题。否则,我开始:

reports.Select(c => new {c.Id, c.PetList}).AsEnumerable() 

这将会从供应商到内存所需的最低限度,从而使LINQ到对象提供者可以从这一点上可以使用。

我需要能够定义一个IEqualityComparer<T>一些T,所以我停止使用匿名类型:

private class IdAndList 
{ 
    public int Id { get; set; } 
    public List<string> PetList { get; set; } 
} 

private class ReportIdAndPetListComparer : IEqualityComparer<IdAndList> 
{ 
    public bool Equals(IdAndList x, IdAndList y) 
    { 
    if (ReferenceEquals(x, y)) return true; 
    if (x == null || y == null) return false; 
    if (x.Id != y.Id) return false; 
    if (x.PetList == null) return y.PetList == null; 
    if (y.PetList == null) return false; 
    int count = x.PetList.Count; 
    if (y.PetList.Count != count) return false; 
    for (int i = 0; i != count; ++i) 
     if (x.PetList[i] != y.PetList[i]) return false; 
    return true; 
    } 

    public int GetHashCode(IdAndList obj) 
    { 
    int hash = obj.Id; 
    if (obj.PetList != null) 
     foreach (string pet in obj.PetList) 
     hash = hash * 31 + pet.GetHashCode(); 
    return hash; 
    } 
} 

有的为空测试PetList s时,可以删除的,如果你知道这是不可能的。

现在:

var viewModel = reports.Select(c => new IdAndList{c.Id, c.PetList}).AsEnumerable() 
    .GroupBy(c => c, new ReportIdAndPetListComparer()) 
    .Select(g => new ArmReportModel 
    { 
     PetList = g.Key.PetList, 
     Pets = g.Count() 
    }); 

或者,如果供应商不能处理构建IdAndPetList类型,那么:

var viewModel = reports.Select(c => new {c.Id, c.PetList}) 
    .AsEnumerable() 
    .Select(c => new IdAndList{c.Id, c.PetList}) 
    .GroupBy(c => c, new ReportIdAndPetListComparer()) 
    .Select(g => new ArmReportModel 
    { 
     PetList = g.Key.PetList, 
     Pets = g.Count() 
    }); 
+0

感谢您的代码!我会在今天早上进行检查,但不会在“g.First()。PetList”列表中列出第一个项目,而不是GroupBy之前的第一个项目? – McArthey

+0

不,它会给你组中的第一个项目的列表。 (如上所述,如果只有每个具有相同'Id'的报告具有相同的列表,否则它不会,并且答案的较长部分变得必要)。 –

+0

这很完美。感谢您提供使用示例。很有帮助。我不得不为Select()稍微调整它,因为它是在抱怨'对象符号',但它正是我所需要的。再次感谢! – McArthey