Group alternate pairs using LINQ












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)
          {
          return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
          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 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; }
          }





          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)
                  {
                  return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
                  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)
                  {
                  return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
                  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)
                  {
                  return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
                  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)
                  {
                  return x.PersonId + "-" + x.RelationId == y.PersonId + "-" + y.RelationId ||
                  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 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; }
                  }





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





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





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






                      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

                                                  MongoDB - Not Authorized To Execute Command

                                                  How to fix TextFormField cause rebuild widget in Flutter

                                                  in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith