C#LINQ Group通过自定义属性的多个字段
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#LINQ Group通过自定义属性的多个字段相关的知识,希望对你有一定的参考价值。
我正在尝试使用Linq聚合多个属性的列表。我的第二个字段是字符串列表+其他字符串列表。
这是我的代码示例:
using System;
using System.Collections.Generic;
using System.Linq;
public class RefValueData
{
public int ReferenceId { get; set; }
public int SiteId { get; set; }
public string SiteName { get; set; }
public string Code { get; set; }
public decimal UnitPoints { get; set; }
public List<TranslationData> Texts { get; set; }
}
public class TranslationData
{
public string Text { get; set; }
public List<TranslationValue> Translations { get; set; }
}
public class TranslationValue
{
public string Culture { get; set; }
public string TranslationText { get; set; }
}
public class Program
{
public static void Main()
{
var values = new List<RefValueData>
{
new RefValueData(){
ReferenceId = 4,
Code = "Code",
SiteId = 2,
SiteName = "Paris",
UnitPoints = 50,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "A",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Bonjour" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Hola" },
}
}
}
},
new RefValueData()
{
ReferenceId = 5,
Code = "Code",
SiteId = 4,
SiteName = "Lyon",
UnitPoints = 50,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "A",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Bonjour" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Hola" },
}
}
}
},
new RefValueData()
{
ReferenceId = 6,
Code = "Code",
SiteId = 3,
SiteName = "Paris",
UnitPoints = 52,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "B",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Salut" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Ciao" },
}
}
}
}
};
var values2 = values
.Distinct()
.GroupBy(x => new
{
x.UnitPoints,
x.Texts
})
.Select(x => new
{
x.Key.UnitPoints,
Texts = x.Key.Texts,
Site = x.Select(y=>y.SiteName)
})
.ToList();
Console.WriteLine(values2.Count);
}
}
我想在我的values2列表中只有两行,但每次它都返回整个列表。
当我只按Unit Point分组时,它的工作非常棒!
我尝试使用一些自定义Linq查询对列表的前两行进行分组,但它根本不起作用...
任何帮助/建议非常感谢:)!
编辑:我也尝试过像这样的Equals方法的覆盖,但我无法使其工作:
public class TranslationValue
{
public string Culture { get; set; }
public string TranslationText { get; set; }
public override bool Equals(object obj)
{
var other = obj as TranslationValue;
if (other == null)
{
return false;
}
return Culture == other.Culture && TranslationText == other.TranslationText;
}
public override int GetHashCode()
{
var hashCode = -2095322044;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Culture);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(TranslationText);
return hashCode;
}
}
public class TranslationData
{
public string Text { get; set; }
public List<TranslationValue> Translations { get; set; }
public override bool Equals(object obj)
{
var other = obj as TranslationData;
if (other == null)
{
return false;
}
return Text == other.Text && Translations.SequenceEqual(other.Translations);
}
public override int GetHashCode()
{
var hashCode = -1551681861;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Text);
hashCode = hashCode * -1521134295 + EqualityComparer<List<TranslationValue>>.Default.GetHashCode(Translations);
return hashCode;
}
}
EDIT2:这是我的“真实”代码:
var values = referenceValues.Select(value => new
{
ReferenceId = value.ReferenceId,
SiteId = value.Reference.SiteId ?? -1,
SiteName = value.Reference.Site.Name ?? allSitesName,
Code = value.Code,
UnitPoints = value.UnitPoints,
Texts = // Type: List<TranslationData> , but it will not use the TranslationDataList class that normally work thanks to your help
value.ReferenceValueTexts.Select(text =>
new TranslationData
{
Text = text.Text, // string
Translations = text.TranslationDataValues.Select(translation => // List<TranslationValue>
new TranslationValue {
Culture = translation.Language.StrCulture,
TranslationText = translation.Value
}).ToList()
}).ToList()
}
朱利安。
这是一个解决方案。它适用于您编写的示例代码。但它需要一些工作才能变得健壮:
// and also change the declarations in the main method to: new TranslationDataList
public class TranslationDataList : List<TranslationData>
{
public override int GetHashCode()
{
int hash = 13;
// string.GetHashCode() is not reliable. This should be an algorithm that returns the same value for two different lists that contain the same data
foreach (var data in this)
hash = (hash * 7) + data.Text.GetHashCode();
return hash;
}
public override bool Equals(object obj)
{
var other = obj as TranslationDataList;
if (other == null) return false;
if (other.Count != Count) return false;
// write the equality logic here. I don't know if it's ok!
for (int i = 0; i < other.Count; i++)
if (other[i].Text != this[i].Text)
return false;
return true;
}
}
首先,您应该向TranslationDataList添加构造函数:
public class TranslationDataList : List<TranslationData>
{
public TranslationDataList(IEnumerable<TranslationData> translationData)
: base(translationData)
{ }
// other members ...
}
现在,您可以在查询中使用TranslationDataList:
var values = referenceValues.Select(value => new
{
ReferenceId = value.ReferenceId,
SiteId = value.Reference.SiteId ?? -1,
SiteName = value.Reference.Site.Name ?? allSitesName,
Code = value.Code,
UnitPoints = value.UnitPoints,
Texts = new TranslationDataList( value.ReferenceValueTexts.Select(text =>
new TranslationData
{
Text = text.Text, // string
Translations = text.TranslationDataValues.Select(translation => // List<TranslationValue>
new TranslationValue {
Culture = translation.Language.StrCulture,
TranslationText = translation.Value
}).ToList()
})); // don't ToList() here anymore
}
这是另一种解决方案:GroupBy方法采用IEqualityComparer,它负责比较分组的项目。但问题是您在分组“GroupBy(x => new {x.UnitPoints,x.Texts})”中使用了匿名类型的键。首先,我们必须创建一个类来发挥关键作用:
public class Key
{
public Key(decimal unitPoints, List<TranslationData> texts)
{
UnitPoints = unitPoints;
Texts = texts;
}
public decimal UnitPoints { get; set; }
public List<TranslationData> Texts { get; set; }
}
然后我们可以实现比较器:
public class Comparer : IEqualityComparer<Key>
{
public bool Equals(Key x, Key y)
{
if (x.UnitPoints != y.UnitPoints) return false;
if (!ListsAreEqual(x.Texts, y.Texts)) return false;
return true;
}
private bool ListsAreEqual(List<TranslationData> x, List<TranslationData> y)
{
if (x.Count != y.Count) return false;
for (int i = 0; i < x.Count; i++)
if (x[i].Text != y[i].Text)
return false;
return true;
}
public int GetHashCode(Key key)
{
int hash = 13;
hash = (hash * 7) + key.UnitPoints.GetHashCode();
foreach (var data in key.Texts)
hash = (hash * 7) + data.Text.GetHashCode();
return hash;
}
}
最后这是你的查询的样子:
var values2 = values
.Distinct()
.GroupBy(x => new Key(x.UnitPoints, x.Texts), new Comparer())
.Select(x => new
{
x.Key.UnitPoints,
Texts = x.Key.Texts,
Site = x.Select(y => y.SiteName)
}).ToList();
我认为第一个解决方案(创建自定义列表类)更好,因为您还可以重构代码并为其提取一些逻辑。
以上是关于C#LINQ Group通过自定义属性的多个字段的主要内容,如果未能解决你的问题,请参考以下文章
LINQ Group使用Lambda通过两个对象属性和SUM,AVG或忽略其他对象属性
C# Linq to sql 实现 group by 统计多字段 返回多字段