2015-03-09 2 views
1

У меня есть вложенный список настраиваемого объекта, переданного из службы в виде JSON, который мне нужен для реструктуризации в новый объект. Вот объект, вложенный список состоит из:Вложенный список LINQ Group для реструктуризации данных

public class Alt 
{ 
    public Member Member { get; set; } 
    public Claim OriginalClaim { get; set; } 
    public Claim AltClaim { get; set; } 
    public double Savings { get; set; } 
} 

Для каждого члена и OriginalClaim, может быть один или более AltClaim. Так что я пытаюсь сделать каждый AltClaim для каждого члена/OriginalClaim. Новый объект, который я буду отображение данных на внешний вид, как это:

public class Alternative 
{ 
    public Member Member { get; set; } 
    public Claim OriginalClaim { get; set; } 
    public List<AlternativeClaim> AlternativeClaims { get; set; } 
} 

public class AlternativeClaim 
{ 
    public Claim AltClaim { get; set; } 
    public double Savings { get; set; } 
} 

Я был в состоянии получить вещи, сгруппированных с помощью LINQ, но производит список Alternative с членом и OriginalClaim дублируется как многие как есть AlternativeClaims. Вот код, который у меня до сих пор:

public void Test(List<List<Alt>> alternatives) 
{ 
    var test = from alts in alternatives 
       from alt in alts 
       group alts by new { alt.Member, alt.OriginalClaim } into a 
       select a; 

    foreach (var a in test) 
    { 
     Console.WriteLine("Auth {0}", a.Key.OriginalClaim.Id); 
     Console.WriteLine("MemberId {0}", a.Key.Member.Id); 

     foreach (var alt in a.SelectMany(x => x)) 
     { 
      [write out alt.AltClaim properties in console here] 
     } 
    } 
} 

Я считаю, что моя проблема связана с запросом LINQ. Я не очень хорошо знаком с LINQ, так есть ли кто-нибудь там, который может помочь мне сформировать это (или, по крайней мере, указать мне в правильном направлении), чтобы я не получал дубликаты? Благодаря!!

EDIT (РАСТВОР):

Решение, которое работало для меня было сочетание двух ответов ниже. The accepted answer - это запрос LINQ, который я использовал, но мне не удалось заставить вещи правильно группироваться без Thomas F's answer. Реализация IEquatable <> в классах, которые я группировал, была ключевой.

Спасибо всем! Вы научили меня многого с вашей помощью по этому вопросу.

+0

Methinks, это опечатка 'группа alts' и должна быть' группа alt'? а также простой выбор вместо selectmany – Grundy

+0

вы можете показать, какой альтернативы есть? Почему список списков? –

+0

@MichalCiechan У вас есть основы того, что альтернатива держится в вашем ответе. Что касается вложенного списка, это то, что возвращается из службы. В основном 'List ' отправляется, и служба возвращает 'List ' для каждого элемента отправленного списка. К сожалению, это запутанно. – Tim

ответ

2
 var alts = alternatives.SelectMany(x => x); 

     var res = alts.GroupBy(alt => Tuple.Create(alt.Member, alt.OriginalClaim)) 
      .Select(g => new Alternative 
      { 
       Member = g.Key.Item1, 
       OriginalClaim = g.Key.Item2, 
       AlternativeClaims = g.Select(x => new AlternativeClaim {AltClaim = x.AltClaim, Savings = x.Savings}).ToList() 
      }); 


     foreach (var a in res) 
     { 
      Console.WriteLine("Auth {0}", a.OriginalClaim.Id); 
      Console.WriteLine("MemberId {0}", a.Member.Id); 

      foreach (var alt in a.AlternativeClaims.OrderByDescending(c => c.Savings)) 
      { 
       Console.WriteLine("\tSavings = {0};Id = {1};",alt.Savings, alt.AltClaim.Id); 
      } 
     } 

дает выход:

Auth 51 
MemberId 50 
    Savings = 696969;Id = 513; 
    Savings = 6969;Id = 512; 
    Savings = 69;Id = 511; 
Auth 52 
MemberId 50 
    Savings = 1002;Id = 522; 
    Savings = 100;Id = 521; 
Auth 102 
MemberId 100 
    Savings = 1022;Id = 1022; 
    Savings = 1021;Id = 1021; 

Код испытания

[Test] 
    public void Test() 
    { 
     var member1 = new Member {Id = 50}; 
     var member1Claim1 = new Claim {Id = 51}; 
     var member1Claim2 = new Claim {Id = 52}; 

     var member2 = new Member { Id = 100 }; 
     var member2Claim1 = new Claim { Id = 101 }; 
     var member2Claim2 = new Claim { Id = 102 }; 

     var alternatives = new List<List<Alt>>() 
     { 
      new List<Alt> 
      { 
       new Alt 
       { 
        Member = member1, 
        OriginalClaim = member1Claim1, 
        AltClaim = new Claim {Id = 511}, 
        Savings = 69 
       }, 
       new Alt 
       { 
        Member = member1, 
        OriginalClaim = member1Claim1, 
        AltClaim = new Claim {Id = 512}, 
        Savings = 6969 
       }, 
       new Alt 
       { 
        Member = member1, 
        OriginalClaim = member1Claim1, 
        AltClaim = new Claim {Id = 513}, 
        Savings = 696969 
       }, 
       new Alt 
       { 
        Member = member1, 
        OriginalClaim = member1Claim2, 
        AltClaim = new Claim {Id = 521}, 
        Savings = 100 
       }, 
       new Alt 
       { 
        Member = member1, 
        OriginalClaim = member1Claim2, 
        AltClaim = new Claim {Id = 522}, 
        Savings = 1002 
       } 
      }, 
      new List<Alt> 
      { 
       new Alt 
       { 
        Member = member2, 
        OriginalClaim = member2Claim2, 
        AltClaim = new Claim {Id = 1021}, 
        Savings = 1021 
       }, 
       new Alt 
       { 
        Member = member2, 
        OriginalClaim = member2Claim2, 
        AltClaim = new Claim {Id = 1022}, 
        Savings = 1022 
       } 
      } 
     }; 


    } 

И для полноты картины:

public class Alt 
{ 
    public Member Member { get; set; } 
    public Claim OriginalClaim { get; set; } 
    public Claim AltClaim { get; set; } 
    public double Savings { get; set; } 
} 

public class Claim 
{ 
    public int Id { get; set; } 
} 

public class Member 
{ 
    public int Id { get; set; } 
} 

public class Alternative 
{ 
    public Member Member { get; set; } 
    public Claim OriginalClaim { get; set; } 
    public List<AlternativeClaim> AlternativeClaims { get; set; } 
} 

public class AlternativeClaim 
{ 
    public Claim AltClaim { get; set; } 
    public double Savings { get; set; } 
} 
+0

Я возился с моим кодом, пытаясь заставить его работать. Это кажется правильным, но вместо того, чтобы мои результаты были такими, какие у вас были, я получаю только один AlternativeClaim для каждого члена и OriginalClaim, поэтому он печатает данные Member/OriginalClaim для каждого AlternativeClaim. Я буду продолжать возиться с ним и сообщить, если я это сделаю. – Tim

+0

Можете ли вы не копировать и вставлять мой код в свой? –

+0

Я сделал, но я чувствую, что что-то странное может происходить с вложенным списком, который я получаю от службы. – Tim

0

Попробуйте alternatives.SelectMany(alt => new { alt.Member, alt.OriginalClaim })

+0

Где в, 'var test ='? – Tim

1

Выполняют ли классы Member и Claim, реализующие IEquatable?

Если нет, я подозреваю, что группировка не работает, поскольку экземпляры Member и Claim сравниваются по ссылке. И поскольку вы упомянули, что получаете данные из службы в качестве JSON, у всех ваших Alts, скорее всего, есть отдельные экземпляры Member и Claim. Поэтому они никогда не попадают в одну и ту же группу, даже если их свойства имеют одинаковые значения.

Чтобы получить вашу группировку, чтобы работать, вы можете сделать одно из следующих действий:

  1. , если оно доступно выбрать уникальный критерий (например, идентификатор или имя) от члена и претензии и группы по ней:

    var test = from alts in alternatives 
          from alt in alts 
          group alts by new { MemberId = alt.Member.Id, OriginalClaimId = alt.OriginalClaim.Id } into a 
          select a; 
    
    foreach (var a in test) 
    { 
        Console.WriteLine("Auth {0}", a.Key.OriginalClaimId); 
        Console.WriteLine("MemberId {0}", a.Key.MemberId); 
    
        foreach (var alt in a.SelectMany(x => x)) 
         [write out alt.AltClaim properties in console here] 
    } 
    
  2. Внесите IEquatable и IEquatable для вашего члена и претензии.

    public class Member : IEquatable<Member> 
    { 
        public bool Equals(Member other) 
        { 
         return Id == other.Id; 
        } 
    
        public override bool Equals(object obj) 
        { 
         var other = obj as Member; 
         if (other == null) 
          return false; 
    
         return Equals(other); 
        } 
    
        public override int GetHashCode() 
        { 
         // Whenever IEquatable is implemented, GetHashCode() must also be overridden. 
         return Id.GetHashCode(); 
        } 
    
        // Rest of the class... 
    } 
    
+0

Это работало 50% пути (пока). Я буду возиться с ним и посмотреть, смогу ли я заставить его работать до конца. У меня есть несколько простых вопросов: (1) В моем фактическом коде есть комбо-ключ в объекте Member. Идентификатор отмечает уникальную группировку, но там есть и вторичный идентификатор. Например, идентификатор участника равен 10099, а вторичный идентификатор может быть 01, 02, 03 и т. Д., Чтобы отметить человека. Как я правильно реализую это в переопределении GetHashCode()? В настоящее время я возвращаю его. Id.GetHashCode() + secondaryId.GetHashCode() ' (2) [в следующем комментарии, закончились символы] – Tim

+0

Вы заметили, что' SelectMany' не требуется во вложенном 'foreach', но' a' в этом случае представляет 'IGrouping <'a,List>'. Есть ли лучший способ попасть в фактический объект 'Alt', чем использовать' SelectMany'? – Tim

+0

(1) Добавление хэш-кодов работает таким образом, но не является оптимальным, так как (x, y) приводит к тому же хэш-коду как (y, x). Простым и часто используемым методом комбинирования хэш-кода из двух numbes является 'Tuple.Create (x, y) .GetHashCode()' –

Смежные вопросы