Group alternate pairs using LINQ
I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplication.
Here is the DTO structure which I have currently which has duplicate rows as you can see which can be grouped together based on reverse relation also.
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
into something like this:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
Code which I am trying:
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();
}
}
RelationDTO.cs
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
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; }
}
c# linq
add a comment |
I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplication.
Here is the DTO structure which I have currently which has duplicate rows as you can see which can be grouped together based on reverse relation also.
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
into something like this:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
Code which I am trying:
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();
}
}
RelationDTO.cs
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
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; }
}
c# linq
add a comment |
I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplication.
Here is the DTO structure which I have currently which has duplicate rows as you can see which can be grouped together based on reverse relation also.
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
into something like this:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
Code which I am trying:
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();
}
}
RelationDTO.cs
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
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; }
}
c# linq
I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplication.
Here is the DTO structure which I have currently which has duplicate rows as you can see which can be grouped together based on reverse relation also.
+----------+------------+-----------+
| PersonId | RelativeId | Relation |
+----------+------------+-----------+
| 1 | 2 | "Son" |
| 2 | 1 | "Father" |
| 1 | 3 | "Mother" |
| 3 | 1 | "Son" |
| 2 | 3 | "Husband" |
| 3 | 2 | "Wife" |
+----------+------------+-----------+
into something like this:
+----------+------------+-----------+-----------------+
| PersonId | RelativeId | Relation | ReverseRelation |
+----------+------------+-----------+-----------------+
| 1 | 2 | "Son" | "Father" |
| 1 | 3 | "Mother" | "Son" |
| 2 | 3 | "Husband" | "Wife" |
+----------+------------+-----------+-----------------+
Code which I am trying:
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();
}
}
RelationDTO.cs
public class RelationDTO
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
public string Relation { get; set; }
}
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; }
}
c# linq
c# linq
edited Jan 3 at 18:12
Peter Mortensen
13.5k1984111
13.5k1984111
asked Jan 3 at 7:14
Kunal MukherjeeKunal Mukherjee
1,2571725
1,2571725
add a comment |
add a comment |
6 Answers
6
active
oldest
votes
I'm not sure whether it is what you need:
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;
}
}
1
can you explain the overloadedequalsmethod?
– Kunal Mukherjee
Jan 3 at 7:54
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
add a comment |
You can use a join operation like
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
}
);
The Key is:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
add a comment |
I doubt a bit that LINQ is the best choice here as a loop with lookup might be a bit more efficient. However if you really need LINQ, then you could do the following
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();
What this code does is joins the collection with itself on the matching pairs PersonId, RelativeId and then filters out the second record of each pair thus resulting in a collection where the first person found in the list will be considered as parent in the relation.
EDIT: The lookup method I was talking about:
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
});
}
}
As a note, it empties your original list so you have to make a copy (list.ToList()) if you need it further in your code.
Running this code turned out to be about six times faster than the method with join I posted before. I also came up with the following grouping method which runs much faster than the join, however it's still slower than the lookup-and-remove method although they do a very similar thing.
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
});
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
add a comment |
var query = relationDTOList.OrderBy(x=>x.PersonId).GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new
{
p.PersonId,
p.RelativeId,
p.Relation,
Parrent = al.Where(x => x.PersonId == p.RelativeId && x.RelativeId == p.PersonId).SingleOrDefault().Relation
}
).ToList();
add a comment |
You could Groupby your relations with a sorted Tuple of PersonId and RelativeId, then pick the first item as first relation and the second item as the reverse relation.
Demo:
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);
}
}
}
Output:
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
add a comment |
This will do it. But it requires duplicates in the original list.
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();
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54017857%2fgroup-alternate-pairs-using-linq%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
I'm not sure whether it is what you need:
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;
}
}
1
can you explain the overloadedequalsmethod?
– Kunal Mukherjee
Jan 3 at 7:54
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
add a comment |
I'm not sure whether it is what you need:
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;
}
}
1
can you explain the overloadedequalsmethod?
– Kunal Mukherjee
Jan 3 at 7:54
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
add a comment |
I'm not sure whether it is what you need:
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;
}
}
I'm not sure whether it is what you need:
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;
}
}
answered Jan 3 at 7:51
ojlovecdojlovecd
3,81111218
3,81111218
1
can you explain the overloadedequalsmethod?
– Kunal Mukherjee
Jan 3 at 7:54
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
add a comment |
1
can you explain the overloadedequalsmethod?
– Kunal Mukherjee
Jan 3 at 7:54
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
1
1
can you explain the overloaded
equals method?– Kunal Mukherjee
Jan 3 at 7:54
can you explain the overloaded
equals method?– Kunal Mukherjee
Jan 3 at 7:54
2
2
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
– ojlovecd
Jan 3 at 8:03
1
1
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
– Taemyr
Jan 3 at 9:49
3
3
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
Also please do't stringify just to compare two ints. It's usually better make two comparisons.
– Taemyr
Jan 3 at 9:51
add a comment |
You can use a join operation like
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
}
);
The Key is:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
add a comment |
You can use a join operation like
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
}
);
The Key is:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
add a comment |
You can use a join operation like
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
}
);
The Key is:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
You can use a join operation like
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
}
);
The Key is:
public struct Key
{
public int PersonId { get; set; }
public int RelativeId { get; set; }
}
answered Jan 3 at 8:27
functorfunctor
1446
1446
add a comment |
add a comment |
I doubt a bit that LINQ is the best choice here as a loop with lookup might be a bit more efficient. However if you really need LINQ, then you could do the following
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();
What this code does is joins the collection with itself on the matching pairs PersonId, RelativeId and then filters out the second record of each pair thus resulting in a collection where the first person found in the list will be considered as parent in the relation.
EDIT: The lookup method I was talking about:
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
});
}
}
As a note, it empties your original list so you have to make a copy (list.ToList()) if you need it further in your code.
Running this code turned out to be about six times faster than the method with join I posted before. I also came up with the following grouping method which runs much faster than the join, however it's still slower than the lookup-and-remove method although they do a very similar thing.
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
});
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
add a comment |
I doubt a bit that LINQ is the best choice here as a loop with lookup might be a bit more efficient. However if you really need LINQ, then you could do the following
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();
What this code does is joins the collection with itself on the matching pairs PersonId, RelativeId and then filters out the second record of each pair thus resulting in a collection where the first person found in the list will be considered as parent in the relation.
EDIT: The lookup method I was talking about:
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
});
}
}
As a note, it empties your original list so you have to make a copy (list.ToList()) if you need it further in your code.
Running this code turned out to be about six times faster than the method with join I posted before. I also came up with the following grouping method which runs much faster than the join, however it's still slower than the lookup-and-remove method although they do a very similar thing.
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
});
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
add a comment |
I doubt a bit that LINQ is the best choice here as a loop with lookup might be a bit more efficient. However if you really need LINQ, then you could do the following
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();
What this code does is joins the collection with itself on the matching pairs PersonId, RelativeId and then filters out the second record of each pair thus resulting in a collection where the first person found in the list will be considered as parent in the relation.
EDIT: The lookup method I was talking about:
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
});
}
}
As a note, it empties your original list so you have to make a copy (list.ToList()) if you need it further in your code.
Running this code turned out to be about six times faster than the method with join I posted before. I also came up with the following grouping method which runs much faster than the join, however it's still slower than the lookup-and-remove method although they do a very similar thing.
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
});
I doubt a bit that LINQ is the best choice here as a loop with lookup might be a bit more efficient. However if you really need LINQ, then you could do the following
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();
What this code does is joins the collection with itself on the matching pairs PersonId, RelativeId and then filters out the second record of each pair thus resulting in a collection where the first person found in the list will be considered as parent in the relation.
EDIT: The lookup method I was talking about:
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
});
}
}
As a note, it empties your original list so you have to make a copy (list.ToList()) if you need it further in your code.
Running this code turned out to be about six times faster than the method with join I posted before. I also came up with the following grouping method which runs much faster than the join, however it's still slower than the lookup-and-remove method although they do a very similar thing.
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
});
edited Jan 3 at 9:22
answered Jan 3 at 8:12
ImantasImantas
775615
775615
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
add a comment |
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
Can you also post the loop lookup method you were talking about?
– Kunal Mukherjee
Jan 3 at 8:45
1
1
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
@KunalMukherjee I added the code and some insights I got by running the different versions.
– Imantas
Jan 3 at 9:21
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
The groupby version is a nice functional way to do it without emptying the list.
– Kunal Mukherjee
Jan 3 at 9:30
add a comment |
var query = relationDTOList.OrderBy(x=>x.PersonId).GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new
{
p.PersonId,
p.RelativeId,
p.Relation,
Parrent = al.Where(x => x.PersonId == p.RelativeId && x.RelativeId == p.PersonId).SingleOrDefault().Relation
}
).ToList();
add a comment |
var query = relationDTOList.OrderBy(x=>x.PersonId).GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new
{
p.PersonId,
p.RelativeId,
p.Relation,
Parrent = al.Where(x => x.PersonId == p.RelativeId && x.RelativeId == p.PersonId).SingleOrDefault().Relation
}
).ToList();
add a comment |
var query = relationDTOList.OrderBy(x=>x.PersonId).GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new
{
p.PersonId,
p.RelativeId,
p.Relation,
Parrent = al.Where(x => x.PersonId == p.RelativeId && x.RelativeId == p.PersonId).SingleOrDefault().Relation
}
).ToList();
var query = relationDTOList.OrderBy(x=>x.PersonId).GroupJoin(relationDTOList,
p => p.PersonId,
a => a.RelativeId,
(p, al) =>
new
{
p.PersonId,
p.RelativeId,
p.Relation,
Parrent = al.Where(x => x.PersonId == p.RelativeId && x.RelativeId == p.PersonId).SingleOrDefault().Relation
}
).ToList();
edited Jan 3 at 8:17
answered Jan 3 at 8:09
ElConradoElConrado
581524
581524
add a comment |
add a comment |
You could Groupby your relations with a sorted Tuple of PersonId and RelativeId, then pick the first item as first relation and the second item as the reverse relation.
Demo:
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);
}
}
}
Output:
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
add a comment |
You could Groupby your relations with a sorted Tuple of PersonId and RelativeId, then pick the first item as first relation and the second item as the reverse relation.
Demo:
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);
}
}
}
Output:
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
add a comment |
You could Groupby your relations with a sorted Tuple of PersonId and RelativeId, then pick the first item as first relation and the second item as the reverse relation.
Demo:
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);
}
}
}
Output:
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
You could Groupby your relations with a sorted Tuple of PersonId and RelativeId, then pick the first item as first relation and the second item as the reverse relation.
Demo:
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);
}
}
}
Output:
PersonId=1 RelativeId=2 Relation=Son ReverseRelation=Father
PersonId=1 RelativeId=3 Relation=Mother ReverseRelation=Son
PersonId=2 RelativeId=3 Relation=Husband ReverseRelation=Wife
edited Jan 3 at 9:13
answered Jan 3 at 8:52
RoadRunnerRoadRunner
11.2k31340
11.2k31340
add a comment |
add a comment |
This will do it. But it requires duplicates in the original list.
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();
add a comment |
This will do it. But it requires duplicates in the original list.
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();
add a comment |
This will do it. But it requires duplicates in the original list.
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();
This will do it. But it requires duplicates in the original list.
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();
answered Jan 3 at 9:04
tomRatzfatztomRatzfatz
113
113
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54017857%2fgroup-alternate-pairs-using-linq%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
