LINQ 组嵌套列表以重构数据

本文关键字:重构 数据 列表 嵌套 LINQ | 更新日期: 2024-11-08 23:25:45

我有一个嵌套列表,其中包含从服务作为 JSON 传递的自定义对象,我需要将其重组为新对象。下面是嵌套列表包含的对象:

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

对于每个成员和原始声明,可以有一个或多个 AltClaim。所以我要做的是为每个成员/原始索赔获取每个 AltClaim。我将将数据映射到的新对象如下所示:

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 对内容进行分组,但它正在生成一个替代列表,其中成员和原始声明的重复次数与替代声明一样多。这是我到目前为止的代码:

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 不是很熟悉,所以有没有人可以帮助我形成这个(或至少为我指出正确的方向),这样我就不会得到重复项?谢谢!!

编辑(解决方案):

对我有用的解决方案是以下两个答案的组合。接受的答案是我使用的 LINQ 查询,但如果没有 Thomas F 的答案,我无法正确分组。在我分组的类上实现 IEquatable<> 是关键。

非常感谢大家!在这个问题上,你在你的帮助下教会了我很多。

LINQ 组嵌套列表以重构数据

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

您的成员类和声明类是否实现了 IEquatable?

如果没有,我怀疑分组不起作用,因为正在通过引用比较成员和声明实例。由于您提到您以 JSON 形式从服务接收数据,因此您的所有 Alt 很可能具有不同的成员和声明实例。因此,即使它们的属性具有相同的值,它们也永远不会属于同一组。

要使分组正常工作,您可以执行以下操作之一:

  1. 如果可用,请从"成员和声明"中选择一个唯一条件(如 ID 或名称),并按该条件进行分组:

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

尝试alternatives.SelectMany(alt => new { alt.Member, alt.OriginalClaim })