Group alternate pairs using Linq












6














I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplicacy.



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





























    6














    I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplicacy.



    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



























      6












      6








      6


      3





      I am trying to group a list of DTOs which contain alternate family pairs to group them in the following format to minimize duplicacy.



      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 duplicacy.



      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 2 hours ago

























      asked 2 hours ago









      Kunal Mukherjee

      1,1521722




      1,1521722
























          6 Answers
          6






          active

          oldest

          votes


















          4














          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





















          • can you explain the overloaded equals method?
            – Kunal Mukherjee
            2 hours ago






          • 1




            In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
            – ojlovecd
            1 hour ago










          • If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
            – Taemyr
            8 mins ago










          • Also please do't stringify just to compare two ints. It's usually better make two comparisons.
            – Taemyr
            7 mins ago



















          5














          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





























            4














            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
              1 hour ago






            • 1




              @KunalMukherjee I added the code and some insights I got by running the different versions.
              – Imantas
              36 mins ago










            • The groupby version is a nice functional way to do it without emptying the list.
              – Kunal Mukherjee
              27 mins ago





















            3














            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































              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














                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























                  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









                  4














                  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





















                  • can you explain the overloaded equals method?
                    – Kunal Mukherjee
                    2 hours ago






                  • 1




                    In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                    – ojlovecd
                    1 hour ago










                  • If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                    – Taemyr
                    8 mins ago










                  • Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                    – Taemyr
                    7 mins ago
















                  4














                  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





















                  • can you explain the overloaded equals method?
                    – Kunal Mukherjee
                    2 hours ago






                  • 1




                    In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                    – ojlovecd
                    1 hour ago










                  • If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                    – Taemyr
                    8 mins ago










                  • Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                    – Taemyr
                    7 mins ago














                  4












                  4








                  4






                  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 2 hours ago









                  ojlovecd

                  3,58611218




                  3,58611218












                  • can you explain the overloaded equals method?
                    – Kunal Mukherjee
                    2 hours ago






                  • 1




                    In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                    – ojlovecd
                    1 hour ago










                  • If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                    – Taemyr
                    8 mins ago










                  • Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                    – Taemyr
                    7 mins ago


















                  • can you explain the overloaded equals method?
                    – Kunal Mukherjee
                    2 hours ago






                  • 1




                    In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                    – ojlovecd
                    1 hour ago










                  • If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                    – Taemyr
                    8 mins ago










                  • Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                    – Taemyr
                    7 mins ago
















                  can you explain the overloaded equals method?
                  – Kunal Mukherjee
                  2 hours ago




                  can you explain the overloaded equals method?
                  – Kunal Mukherjee
                  2 hours ago




                  1




                  1




                  In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                  – ojlovecd
                  1 hour ago




                  In order to distinct the result list. Because 1-2 should be equal to 2-1, we have to do the equalizer ourselves.
                  – ojlovecd
                  1 hour ago












                  If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                  – Taemyr
                  8 mins ago




                  If you assume that the original list is unique you can get a distinct list by filtering such that personId<RelationId.
                  – Taemyr
                  8 mins ago












                  Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                  – Taemyr
                  7 mins ago




                  Also please do't stringify just to compare two ints. It's usually better make two comparisons.
                  – Taemyr
                  7 mins ago













                  5














                  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


























                    5














                    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
























                      5












                      5








                      5






                      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 1 hour ago









                      functor

                      1145




                      1145























                          4














                          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
                            1 hour ago






                          • 1




                            @KunalMukherjee I added the code and some insights I got by running the different versions.
                            – Imantas
                            36 mins ago










                          • The groupby version is a nice functional way to do it without emptying the list.
                            – Kunal Mukherjee
                            27 mins ago


















                          4














                          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
                            1 hour ago






                          • 1




                            @KunalMukherjee I added the code and some insights I got by running the different versions.
                            – Imantas
                            36 mins ago










                          • The groupby version is a nice functional way to do it without emptying the list.
                            – Kunal Mukherjee
                            27 mins ago
















                          4












                          4








                          4






                          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 36 mins ago

























                          answered 1 hour ago









                          Imantas

                          641315




                          641315












                          • Can you also post the loop lookup method you were talking about?
                            – Kunal Mukherjee
                            1 hour ago






                          • 1




                            @KunalMukherjee I added the code and some insights I got by running the different versions.
                            – Imantas
                            36 mins ago










                          • The groupby version is a nice functional way to do it without emptying the list.
                            – Kunal Mukherjee
                            27 mins ago




















                          • Can you also post the loop lookup method you were talking about?
                            – Kunal Mukherjee
                            1 hour ago






                          • 1




                            @KunalMukherjee I added the code and some insights I got by running the different versions.
                            – Imantas
                            36 mins ago










                          • The groupby version is a nice functional way to do it without emptying the list.
                            – Kunal Mukherjee
                            27 mins ago


















                          Can you also post the loop lookup method you were talking about?
                          – Kunal Mukherjee
                          1 hour ago




                          Can you also post the loop lookup method you were talking about?
                          – Kunal Mukherjee
                          1 hour ago




                          1




                          1




                          @KunalMukherjee I added the code and some insights I got by running the different versions.
                          – Imantas
                          36 mins ago




                          @KunalMukherjee I added the code and some insights I got by running the different versions.
                          – Imantas
                          36 mins ago












                          The groupby version is a nice functional way to do it without emptying the list.
                          – Kunal Mukherjee
                          27 mins ago






                          The groupby version is a nice functional way to do it without emptying the list.
                          – Kunal Mukherjee
                          27 mins ago













                          3














                          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














                            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












                              3








                              3






                              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 1 hour ago

























                              answered 1 hour ago









                              ElConrado

                              572524




                              572524























                                  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 53 mins ago









                                      tomRatzfatz

                                      112




                                      112























                                          1














                                          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














                                            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












                                              1








                                              1






                                              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 45 mins ago

























                                              answered 1 hour ago









                                              RoadRunner

                                              10.5k31340




                                              10.5k31340






























                                                  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.





                                                  Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                                                  Please pay close attention to the following guidance:


                                                  • 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

                                                  Understanding the information contained in the Deep Space Network XML data?

                                                  Ross-on-Wye

                                                  Eastern Orthodox Church