Reputation: 301
I have a nested list of a custom object passed from a service as JSON that I need to restructure into a new object. Here is the object that the nested list consists of:
public class Alt
{
public Member Member { get; set; }
public Claim OriginalClaim { get; set; }
public Claim AltClaim { get; set; }
public double Savings { get; set; }
}
For each Member and OriginalClaim, there can be one or more AltClaim. So what I'm trying to do is get every AltClaim for each Member/OriginalClaim. The new object that I will be mapping the data to looks like this:
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; }
}
I've been able to get things grouped via LINQ, but it is producing a list of Alternative with the Member and OriginalClaim duplicated as many times as there are AlternativeClaims. Here is the code I've got so far:
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]
}
}
}
I believe my issue is with the LINQ query. I'm not terribly familiar with LINQ, so is there anyone out there that can help me form this (or at least point me in the right direction) so I don't get the duplicates? Thanks!!
EDIT (SOLUTION):
The solution that worked for me was a combination of two answers below. The accepted answer is the LINQ query I used, but I wasn't able to get things to group properly without Thomas F's answer. Implementing IEquatable<> on the classes I was grouping by was key.
Thank you all so much! You've taught me a lot with your help on this issue.
Upvotes: 1
Views: 450
Reputation: 737
Do your Member and Claim classes implement IEquatable?
If not, I suspect the grouping does not work because the Member and Claim instances are being compared by reference. And since you have mentioned that you receive the data from a service as JSON, all of your Alts most likely have distinct Member and Claim instances. Therefore, they never fall into the same group, even if their properties have the same values.
To get your grouping to work, you could do one of the following:
If available, choose a unique criterion (like an id or name) from Member and Claim and group by it:
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]
}
Implement IEquatable and IEquatable for your Member and Claim.
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...
}
Upvotes: 1
Reputation: 13888
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);
}
}
gives output:
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 code
[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
}
}
};
}
And for completeness:
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; }
}
Upvotes: 2
Reputation: 748
Try alternatives.SelectMany(alt => new { alt.Member, alt.OriginalClaim })
Upvotes: 0