【问题标题】:linq Groupby a list by "sublist"linq Groupby 按“子列表”列出的列表
【发布时间】:2018-06-16 21:09:22
【问题描述】:

我有一个List<Meb>(一个条形嵌套),每个嵌套内部都有一个详细信息列表。
所有这些条形图都是唯一的,因为其中的每个元素的 ID 都是唯一的。

现在我想添加一个复选框,以便对内部具有相同详细信息列表的所有条进行分组或不分组(里面的项目列表是相同的,除了它们的 ID 和一些我首先设置为 -1 或“”)。这是我为了做到这一点而制作的功能:

private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
    List<Meb> retour = new List<Meb>();
    foreach(Meb mebOri in mebInput)
    {
        Meb meb = new Meb();
        meb.ID = -1;
        meb.Number = mebOri.Number;
        meb.Length = mebOri.Length;
        meb.Quantity=mebOri.Quantity;
        foreach(Repere repOri in mebOri.ListReperes)
        {
            Repere rep = new Repere();
            rep.Name = repOri.Name;
            rep.Quantite = repOri.Quantite;
            rep.ID = -1;
            meb.ListReperes.Add(rep);
        }
        retour.Add(meb);


    }
    retour = retour.GroupBy(l => l.ListReperes)
            .Select(cl => new Meb
            {
                ID=-1,
                Number = cl.First().Number,
                Length = cl.First().Length,
                Quantity=cl.Sum(c => c.Quantity),
                ListReperes = cl.First().ListReperes,
            }).ToList();
    return retour;
}

这个想法是:

第一个:我创建了一个新的List&lt;Meb&gt;,它复制了原始List&lt;Meb&gt;,对于List&lt;Repere&gt;,我也复制了它,但将ID设置为“-1”,因为其他属性可能不同。

第二个:我在List&lt;Repere&gt;上进行了分组

但是最后没有分组,输出和输入一样。

编辑:

我更好地解释了我的对象的结构,因为它似乎不够清楚:

每个 Meb 对象代表一个梁,每个梁包含 Repere 对象(细节),这些细节有很多参数,最重要的是 ID、名称、数量,具体示例:

                           ID    Name        Quantity

Meb1(Quantity1) contains : 11    Repere1     2
                           20    Repere2     1
                           25    Repere3     1

Meb2(Quantity2) contains : 12    Repere1     2
                           24    Repere2     2
                           28    Repere3     1

Meb3(Quantity3) contains : 31    Repere1     2
                           18    Repere2     1
                           55    Repere3     1

So I import my List<Meb>, and I want to group all my Mebs, comparing their details list.
In that case the result would be :

Meb1(Quantity4) contains : 0    Repere1     2
                           0    Repere2     1
                           0    Repere3     1

Meb2(Quantity2) contains : 0    Repere1     2
                           0    Repere2     2
                           0    Repere3     1

【问题讨论】:

  • 我建议尝试使示例更简单。很难遵循你真正想要实现的目标。在我看来,您正在尝试在数组上执行 GroupBy,这不是 GroupBy 的完成方式。
  • 好的,谢谢。事实上,我尝试在对象列表上创建一个 groupby——我的 list 有一个 列表,每个 Meb 都有一个 List,如果 list 相同,那么我组。我第一次尝试做这样的事情,但不确定是否可以做到?

标签: c# linq


【解决方案1】:

我建议您在 Meb 类中添加某种属性,以散列您的所有 ListReperes 项目,然后将其分组。

你可以看看这个链接:How to generate a unique hash for a collection of objects independent of their order

IE 那么你会这样做: retour = retour.GroupBy(l => l.HashReperes) 这将为您提供一个独特的列表分组列表。

其中 HashReperes 是提供 Reperes List 哈希的属性。

【讨论】:

  • 好的,所以如果我理解你的答案,我不能按列表对列表进行分组?在互联网上阅读了一下之后,HashCode 对我来说似乎是一个很好的解决方案,但我认为即使列表是相同的,它们生成不同的哈希码。我所做的是 foreach(Meb meb in listeMeb) meb.ID= meb.ListReperes.GetHashCode();我理解对了吗?
  • 由于 HashCode 没有按预期工作,我创建了自己的函数:我添加了一些字符串属性,在其中连接 ListReperes 的每个元素(我只连接我想要比较的参数,如名称和数量)。所以在我为所有这些字符串都设置了相同的字符串之后。但是在分组时我遇到了一个新错误,即对象可能是 IComparable(我认为它加入了 jdweng 答案)。但是为什么它们需要是 IComparable 呢?我已经在不是 IComparable 的对象列表上使用了 groupby?还是因为这次我里面也有列表?
  • Meb 不是 List 对象,它是一个类。比较需要知道你只是在比较数量和长度;而不是 ID 和数量。
  • jdweng 也许我认为你没有理解这个问题(或者我解释得不够好),我想比较的不是数量和长度,而是一个 List。就我而言,我想对 List 进行分组,每个 Meb 对象都包含一个 List,这就是我要比较的 List。最后我无法使用你的答案(比较列表),所以我使用了 Ilan Keshet 的提示(在我之前的评论中解释过)。我会把它作为我帖子的编辑(或者作为答案,如果你可以确认这样做是正确的)。
【解决方案2】:

使用 IEquatable。然后你可以使用标准的 linq GroupBy()。见下面的代码

    public class Meb : IEquatable<Meb>, INotifyPropertyChanged
    {
        public int ID { get; set; }
        public int Number { get; set; }
        public int Length { get; set; }
        public int Quantity { get; set;}

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
        {

            return mebInput.GroupBy(x => x).Select(x => new Meb() {
                ID = x.First().ID,
                Number = x.First().Number,
                Length = x.First().Length,
                Quantity = x.Sum(y => y.Quantity)
            }).ToList();

        }

        public bool Equals(Meb other)
        {
            if ((this.Number == other.Number) && (this.Length == other.Length) && (this.Quantity == other.Quantity))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override int GetHashCode()
        {
            return ID;
        }
    }

如果你不想使用 IEquatable 那就用这个

       private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
        {

            return mebInput.GroupBy(x => new { number = x.Number, len = x.Length }).Select(x => new Meb()
            {
                ID = x.First().ID,
                Number = x.Key.number,
                Length = x.Key.len,
                Quantity = x.Sum(y => y.Quantity)
            }).ToList();

        }

为了比较一个列表,使用类似这样的东西

    public class MyClassA : IEquatable<List<MyClassB>>
    {
        public List<MyClassB> myClassB { get; set; }

        public bool Equals(List<MyClassB> other)
        {
            if(other == null) return false;
            if (this.myClassB.Count() != other.Count()) return false;

            var groupThis = this.myClassB.OrderBy(x => x.propertyA).ThenBy(x => x.propertyB).GroupBy(x => x).ToList();
            var groupOther = other.OrderBy(x => x.propertyA).ThenBy(x => x.propertyB).GroupBy(x => x).ToList();

            if (groupThis.Count() != groupOther.Count) return false;

            for (int i = 0; i < groupThis.Count(); i++)
            {
                if (groupThis[i].Count() != groupOther[i].Count()) return false;
            }
            return true;
        }
        public override int GetHashCode()
        {
            return 0;
        }
    }
    public class MyClassB : IEquatable<MyClassB>
    {
        public int propertyA { get; set; }
        public string propertyB { get; set; }

        public bool Equals(MyClassB other)
        {
            if (other == null) return false;

            if ((this.propertyA == other.propertyA) && (this.propertyB == other.propertyB))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override int GetHashCode()
        {
            return 0;
        }

    }

【讨论】:

  • 也尝试了你的建议,但是我的课程已经是 INotifyPropertyChanged,如果我将其设置为公共课程 Meb : INotifyPropertyChanged IEquatable,我有一个语法错误(那么正确的语法是什么?)
  • 你可能只是少了一个逗号。
  • 现在我收到一个错误“必须在一个对象中实现至少一个 IComparable 接口。”(从俄语翻译,所以我不知道是否正确。关于 Equals 函数,它是自动分组时调用?最后是 GetHashCode,我不明白它是如何工作的。还注意到我的代码和你的代码之间的主要区别是我比较了一个 List,当我在对象中没有任何列表时, groupby 工作正常,仅在对象包含 List 时遇到问题
  • 哈希码在这个例子中真的没有多大作用。你可以只返回零,它会工作。 linq 自动为 GroupBy 调用类 Equals 方法。您可以在 Equals 方法中添加对 null 的测试。请参阅 msdn 示例:msdn.microsoft.com/en-us/library/ms131190(v=vs.110).aspx。查看 msdn 示例,看看它是否提供了有关错误的线索。我认为您在代码顶部缺少“使用”。我将发布一种不使用 IEquatable 的方法。
  • 好吧,我刚刚明白,当我做 GroupBy(l => l) 时使用了 Equals 函数。我可能错过了一些东西,因为我相信 Equals 函数会被 groupby 调用,然后我看到首先 groupby 完成(没有分组任何东西),并且只有在“return retour”之后,Equals 才被调用 x 次,并且“其他”始终为空(other.Count=0)。刚看到你的编辑,那就试试这个,谢谢
【解决方案3】:

最后,这是我可以解决问题的方法:

private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
    List<Meb> retour = new List<Meb>();
    foreach(Meb mebOri in mebInput)
    {
        Meb meb = new Meb();
        meb.ID = -1;
        meb.Number = mebOri.Number;
        meb.Length = mebOri.Length;
        meb.Quantity=mebOri.Quantity;
        foreach(Repere repOri in mebOri.ListReperes)
        {
            Repere rep = new Repere();
            rep.Name = repOri.Name;
            rep.Quantite = repOri.Quantite;
            rep.ID = -1;
            meb.ListReperes.Add(rep);
        }
        retour.Add(meb);
        // Here I added a string property, in which I concatenate 
        //name and quantity of each Repere in my List<Repere>, 
        //so on the end the "SomeString" parameters will be identical
        //for all Meb that have the same List<Repere> (ignoring the IDs).
        foreach(Meb meb in retour)
        {
            meb.SomeString = "";
            foreach(RepereNest rep in meb.ListReperes)
            {
                meb.SomeString += rep.Name + rep.Quantite;
            }
        }


    }
    retour = retour.GroupBy(l => l.SomeString)
            .Select(cl => new Meb
            {
                ID=-1,
                Number = cl.First().Number,
                SomeString=cl.First().SomeString,
                Length = cl.First().Length,
                Quantity=cl.Sum(c => c.Quantity),
                ListReperes = cl.First().ListReperes,
            }).ToList();
    return retour;
}

嗯,现在这是我能找到的唯一方法,不是在我的参数上进行分组(为此没问题),而是在我的对象列表中的参数上。而且我认为这种方法还不错,因为我在 Repere 对象中也有列表,所以我将来可以使用相同的技巧。最后我只是不明白为什么无法检查我的对象列表何时相等?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多