C# 使用LINQ将备用对分组
我正在尝试对包含备用族对的列表进行分组,以便按以下格式对它们进行分组,以最大限度地减少重复 这里是我目前拥有的DTO结构,它有重复的行,正如您可以看到的,这些行也可以根据反向关系分组在一起C# 使用LINQ将备用对分组,c#,linq,C#,Linq,我正在尝试对包含备用族对的列表进行分组,以便按以下格式对它们进行分组,以最大限度地减少重复 这里是我目前拥有的DTO结构,它有重复的行,正如您可以看到的,这些行也可以根据反向关系分组在一起 +----------+------------+-----------+ | PersonId | RelativeId | Relation | +----------+------------+-----------+ | 1 | 2 | "Son" | |
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
变成这样:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
我正在尝试的代码: Program.cs
class Program
{
static void Main(string[] args)
{
List<RelationDTO> relationDTOList = new List<RelationDTO>
{
new RelationDTO { PersonId = 1, RelativeId = 2, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 1, Relation = "Father" },
new RelationDTO { PersonId = 1, RelativeId = 3, Relation = "Mother" },
new RelationDTO { PersonId = 3, RelativeId = 1, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 3, Relation = "Husband" },
new RelationDTO { PersonId = 3, RelativeId = 2, Relation = "Wife" },
};
var grp = relationDTOList.GroupBy(x => new { x.PersonId }).ToList();
}
}
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
public class Relations
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
public string ReverseRelation { get; set; }
}
关系。cs
class Program
{
static void Main(string[] args)
{
List<RelationDTO> relationDTOList = new List<RelationDTO>
{
new RelationDTO { PersonId = 1, RelativeId = 2, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 1, Relation = "Father" },
new RelationDTO { PersonId = 1, RelativeId = 3, Relation = "Mother" },
new RelationDTO { PersonId = 3, RelativeId = 1, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 3, Relation = "Husband" },
new RelationDTO { PersonId = 3, RelativeId = 2, Relation = "Wife" },
};
var grp = relationDTOList.GroupBy(x => new { x.PersonId }).ToList();
}
}
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
public class Relations
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
public string ReverseRelation { get; set; }
}
我不确定这是否是您需要的:
public static void Main()
{
List<RelationDTO> relationDTOList = new List<RelationDTO>
{
new RelationDTO { PersonId = 1, RelativeId = 2, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 1, Relation = "Father" },
new RelationDTO { PersonId = 1, RelativeId = 3, Relation = "Mother" },
new RelationDTO { PersonId = 3, RelativeId = 1, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 3, Relation = "Husband" },
new RelationDTO { PersonId = 3, RelativeId = 2, Relation = "Wife" },
};
var grp = relationDTOList.Join(relationDTOList,
dto => dto.PersonId + "-" + dto.RelativeId,
dto => dto.RelativeId + "-" + dto.PersonId,
(dto1, dto2) => new Relations
{
PersonId = dto1.PersonId,
RelationId = dto1.RelativeId,
Relation = dto1.Relation,
ReverseRelation = dto2.Relation
}).Distinct(new MyEqualityComparer());
foreach (var g in grp)
Console.WriteLine("{0},{1},{2},{3}", g.PersonId, g.RelationId, g.Relation, g.ReverseRelation);
}
public class MyEqualityComparer : IEqualityComparer<Relations>
{
public bool Equals(Relations x, Relations y)
{
return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;
}
public int GetHashCode(Relations obj)
{
return 0;
}
}
publicstaticvoidmain()
{
列表关系列表=新列表
{
新关系到{PersonId=1,RelativeId=2,relative=“Son”},
新关系到{PersonId=2,RelativeId=1,relative=“Father”},
新关系到{PersonId=1,RelativeId=3,relative=“Mother”},
新关系到{PersonId=3,RelativeId=1,relative=“Son”},
与{PersonId=2,RelativeId=3,relative=“丈夫”}的新关系,
新关系到{PersonId=3,RelativeId=2,relative=“妻子”},
};
var grp=relationDTOList.Join(relationDTOList,
dto=>dto.PersonId+“-”+dto.RelativeId,
dto=>dto.RelativeId+“-”+dto.PersonId,
(dto1,dto2)=>新关系
{
PersonId=dto1.PersonId,
RelationId=dto1.RelativeId,
关系=dto1.关系,
反向关联=dto2.关系
}).Distinct(新MyEqualityComparer());
foreach(grp中的var g)
Console.WriteLine(“{0},{1},{2},{3}”,g.PersonId,g.RelationId,g.relational,g.ReverseRelation);
}
公共类MyQualityComparer:IEqualityComparer
{
公共布尔等于(关系x,关系y)
{
返回x.PersonId+“-”+x.RelationId==y.PersonId+“-”+y.RelationId |
x、 PersonId+“-”+x.RelationId==y.RelationId+“-”+y.PersonId;
}
公共int GetHashCode(关系对象)
{
返回0;
}
}
我有点怀疑LINQ是否是这里的最佳选择,因为带查找的循环可能更高效一些。但是,如果您真的需要LINQ,那么您可以执行以下操作
var relations = from person in relationDTOList
// Match on the exact pair of IDs
join relative in relationDTOList on
new { person.PersonId, person.RelativeId } equals
new { PersonId = relative.RelativeId, RelativeId = relative.PersonId }
// Build the new structure
let relation = new Relations {
PersonId = person.PersonId,
Relation = person.Relation,
RelativeId = relative.PersonId,
ReverseRelation = relative.Relation
}
// Order the pairs to find the duplicates
let ids = new[] {person.PersonId, relative.PersonId}.OrderBy(x => x).ToArray()
group relation by new { FirstPersonId = ids[0], SecondPersonId = ids[1] }
into relationGroups
// Select only the the first of two duplicates
select relationGroups.First();
此代码所做的是在匹配的对PersonId
,RelativeId
上将集合与自身连接,然后过滤掉每对的第二条记录,从而生成一个集合,其中在列表中找到的第一个人将被视为关系中的父对象
编辑:我刚才提到的查找方法:
var result = new List<Relations>();
while (relationDTOList.Any())
{
var person = relationDTOList.First();
relationDTOList.RemoveAt(0);
var relative = relationDTOList.Where(x =>
x.PersonId == person.RelativeId && x.RelativeId == person.PersonId)
.Select((x, i) => new {Person = x, Index = i}).FirstOrDefault();
if (relative != null)
{
relationDTOList.RemoveAt(relative.Index);
result.Add(new Relations {
PersonId = person.PersonId,
Relation = person.Relation,
RelativeId = relative.Person.PersonId,
ReverseRelation = relative.Person.Relation
});
}
}
您可以使用如下的联接操作
var result = relationDTOList
.Where(v => v.PersonId < v.RelativeId)
.Join(
relationDTOList.Where(v => v.PersonId > v.RelativeId),
v => new Key{PersonId = v.PersonId, RelativeId = v.RelativeId},
v => new Key{PersonId = v.RelativeId, RelativeId = v.PersonId},
(p, q) => new Relations
{
PersonId = p.PersonId,
RelativeId = p.RelativeId,
Relation = p.Relation,
ReverseRelation = q.Relation
}
);
您可以通过
Groupby
将您与PersonId
和RelativeId
的已排序元组的关系进行分组,然后选择第一项作为第一个关系,选择第二项作为反向关系
演示:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example {
public static class Program {
public static void Main (string[] args) {
List<RelationDTO> relationDTOList = new List<RelationDTO> {
new RelationDTO { PersonId = 1, RelativeId = 2, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 1, Relation = "Father" },
new RelationDTO { PersonId = 1, RelativeId = 3, Relation = "Mother" },
new RelationDTO { PersonId = 3, RelativeId = 1, Relation = "Son" },
new RelationDTO { PersonId = 2, RelativeId = 3, Relation = "Husband" },
new RelationDTO { PersonId = 3, RelativeId = 2, Relation = "Wife" },
};
// Group relations into list of lists
var groups = relationDTOList
.GroupBy (r => GetOrderedTuple (r.PersonId, r.RelativeId))
.Select (grp => grp.ToList ()).ToList ();
// Output original relations and their reverse relations
foreach (var group in groups) {
var relation = group.ElementAt (0);
var reverseRelation = group.ElementAt (1);
FormattableString relationOutput = $"PersonId={relation.PersonId} RelativeId={relation.RelativeId} Relation={relation.Relation} ReverseRelation={reverseRelation.Relation}";
Console.WriteLine (relationOutput);
}
}
private static Tuple<int, int> GetOrderedTuple (int n1, int n2) {
if (n1 < n2) {
return Tuple.Create (n1, n2);
}
return Tuple.Create (n2, n1);
}
}
}
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
这就行了。但它需要原始列表中的副本
var result = relationDTOList
.Where(v => v.PersonId < v.RelativeId)
.GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new{
p.PersonId,
p.RelativeId,
p.Relation,
ReverseRelation = al.Where( x =>
x.PersonId == p.RelativeId &&
x.RelativeId == p.PersonId )
.SingleOrDefault()
.Relation} ).ToList();
var result=relationDTOList
.其中(v=>v.PersonIdp.PersonId,
a=>a,
(p,al)=>
新的{
p、 人格,
p、 相对论,
p、 关系,
反向关联=al.其中(x=>
x、 PersonId==p.RelativeId&&
x、 RelativeId==p.PersonId)
.SingleOrDefault()
.Relation}).ToList();
为了区分结果列表,您可以解释重载的equals
方法吗。因为1-2应该等于2-1,我们必须自己做均衡器。如果你假设原始列表是唯一的,你可以通过过滤得到一个不同的列表,这样personIdAlso请不要仅仅为了比较两个整数而进行字符串化。通常最好进行两次比较。你也可以发布你所说的循环查找方法吗?@KunalMukherjee我添加了代码以及通过运行不同版本获得的一些见解。groupby版本是一种很好的功能方式,可以在不清空列表的情况下完成这项工作。