Group alternate pairs using LINQ

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












11















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;










share|improve this question




























    11















    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;










    share|improve this question


























      11












      11








      11


      3






      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;










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      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






















          6 Answers
          6






          active

          oldest

          votes


















          7














          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)

          x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;


          public int GetHashCode(Relations obj)

          return 0;







          share|improve this answer


















          • 1





            can you explain the overloaded equals method?

            – 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


















          8














          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 KeyPersonId = v.PersonId, RelativeId = v.RelativeId,
          v => new KeyPersonId = 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;






          share|improve this answer






























            6














            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
            );





            share|improve this answer

























            • 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



















            4














            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();





            share|improve this answer
































              3














              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





              share|improve this answer
































                1














                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();





                share|improve this answer






















                  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
                  );



                  );













                  draft saved

                  draft discarded


















                  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









                  7














                  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)

                  x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;


                  public int GetHashCode(Relations obj)

                  return 0;







                  share|improve this answer


















                  • 1





                    can you explain the overloaded equals method?

                    – 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















                  7














                  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)

                  x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;


                  public int GetHashCode(Relations obj)

                  return 0;







                  share|improve this answer


















                  • 1





                    can you explain the overloaded equals method?

                    – 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













                  7












                  7








                  7







                  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)

                  x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;


                  public int GetHashCode(Relations obj)

                  return 0;







                  share|improve this answer













                  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)

                  x.PersonId + "-" + x.RelationId == y.RelationId + "-" + y.PersonId;


                  public int GetHashCode(Relations obj)

                  return 0;








                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jan 3 at 7:51









                  ojlovecdojlovecd

                  3,81111218




                  3,81111218







                  • 1





                    can you explain the overloaded equals method?

                    – 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





                    can you explain the overloaded equals method?

                    – 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













                  8














                  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 KeyPersonId = v.PersonId, RelativeId = v.RelativeId,
                  v => new KeyPersonId = 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;






                  share|improve this answer



























                    8














                    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 KeyPersonId = v.PersonId, RelativeId = v.RelativeId,
                    v => new KeyPersonId = 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;






                    share|improve this answer

























                      8












                      8








                      8







                      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 KeyPersonId = v.PersonId, RelativeId = v.RelativeId,
                      v => new KeyPersonId = 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;






                      share|improve this answer













                      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 KeyPersonId = v.PersonId, RelativeId = v.RelativeId,
                      v => new KeyPersonId = 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;







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Jan 3 at 8:27









                      functorfunctor

                      1446




                      1446





















                          6














                          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
                          );





                          share|improve this answer

























                          • 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
















                          6














                          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
                          );





                          share|improve this answer

























                          • 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














                          6












                          6








                          6







                          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
                          );





                          share|improve this answer















                          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
                          );






                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          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


















                          • 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












                          4














                          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();





                          share|improve this answer





























                            4














                            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();





                            share|improve this answer



























                              4












                              4








                              4







                              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();





                              share|improve this answer















                              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();






                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited Jan 3 at 8:17

























                              answered Jan 3 at 8:09









                              ElConradoElConrado

                              581524




                              581524





















                                  3














                                  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





                                  share|improve this answer





























                                    3














                                    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





                                    share|improve this answer



























                                      3












                                      3








                                      3







                                      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





                                      share|improve this answer















                                      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






                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited Jan 3 at 9:13

























                                      answered Jan 3 at 8:52









                                      RoadRunnerRoadRunner

                                      11.2k31340




                                      11.2k31340





















                                          1














                                          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();





                                          share|improve this answer



























                                            1














                                            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();





                                            share|improve this answer

























                                              1












                                              1








                                              1







                                              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();





                                              share|improve this answer













                                              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();






                                              share|improve this answer












                                              share|improve this answer



                                              share|improve this answer










                                              answered Jan 3 at 9:04









                                              tomRatzfatztomRatzfatz

                                              113




                                              113



























                                                  draft saved

                                                  draft discarded
















































                                                  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.




                                                  draft saved


                                                  draft discarded














                                                  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





















































                                                  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






                                                  Popular posts from this blog

                                                  Peggy Mitchell

                                                  Palaiologos

                                                  The Forum (Inglewood, California)