我正在尝试对包含备用族对的DTO列表进行分组,以便按以下格式对它们进行分组,以最大限度地减少重复.
这是我目前的DTO结构,它有重复的行,你可以看到它们也可以根据反向关系组合在一起.
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
Run Code Online (Sandbox Code Playgroud)
进入这样的事情:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
Run Code Online (Sandbox Code Playgroud)
我正在尝试的代码:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
RelationDTO.cs
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Relations.cs
public class Relations
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
public string ReverseRelation { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
小智 8
您可以使用类似的连接操作
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
}
);
Run Code Online (Sandbox Code Playgroud)
该Key
方法是:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我不确定这是否是你需要的:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我怀疑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();
Run Code Online (Sandbox Code Playgroud)
此代码所做的是将集合与自身连接在匹配对上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
});
}
}
Run Code Online (Sandbox Code Playgroud)
作为一个注释,它会清空您的原始列表,因此list.ToList()
如果您需要在代码中进一步需要,则必须复制().
运行此代码的速度比join
我之前发布的方法快六倍.我还提出了以下分组方法,它比连接运行得快得多,但它仍然比查找和删除方法慢,尽管它们做的非常类似.
var relations = relationDTOList.GroupBy(person =>
person.PersonId < person.RelativeId
? new {FirstPersonId = person.PersonId, SecondPersonId = person.RelativeId}
: new {FirstPersonId = person.RelativeId, SecondPersonId = person.PersonId})
.Select(group => new Relations {
PersonId = group.First().PersonId,
Relation = group.First().Relation,
RelativeId = group.First().RelativeId,
ReverseRelation = group.Last().Relation
});
Run Code Online (Sandbox Code Playgroud)