分组和单独列表

Group and separate list

我有以下实体结构

public class Item
{
    public EnumType Type { get; set; }
    public int Price { get; set; }

}

public enum EnumType
{
    A =1,
    B=2,
    C =3
}

我有如下物品清单

   var items = new List<Item>
        {                 
            new Item{ Price=5, Type= EnumType.B},
            new Item{ Price=5, Type= EnumType.B},
            new Item{ Price=5, Type= EnumType.B},
            new Item{ Price=10, Type= EnumType.B},
            new Item{ Price=10, Type= EnumType.B},
            new Item{ Price=10, Type= EnumType.B},
            new Item{ Price=15, Type= EnumType.C},
            new Item{ Price=15, Type= EnumType.C},
            new Item{ Price=15, Type= EnumType.C},
            new Item{ Price=15, Type= EnumType.C},
            new Item{ Price=15, Type= EnumType.C}
        };

如果价格和类型相同,则需要根据类型从列表中排除每第n项,然后计算总和。 即类型 B = 3,类型 C = 4

这意味着在上面的示例数据中,由于类型 B 中有 3 个项目,一旦按价格和类型分组,因此在计算总和时需要排除每第 3 个项目。 所以类型 B 的总和将为 5+5+10+10,类型 C 的总和将为 15+15+15+15

我试过使用模块化,但似乎方向不对

到目前为止我已经试过了

  static int GetFactorByType(EnumType t)
    {
        switch(t)
        {
            case EnumType.A:
                return 2;
            case EnumType.B:
                return 3;
            case EnumType.C:
                return 4;
            default:
                return 2;
        }
    }

  var grp = items.GroupBy(g => new { g.Type, g.Price }).Select(s => new
        {
           type= s.Key.Type,
           price = s.Key.Price,
           count = s.Count()
        }).Where(d => d.count % GetFactorByType(d.type) == 0).ToList();

按始终唯一的新对象分组可保证您拥有的组数与项目数一样多。尝试这样的事情:

var grp = items.GroupBy(g => $"{g.Type}/{g.Price}").Select(s => new
    {
        type= s.Value.First().Type,
        price = s.Value.First().Price,
        count = s.Value.Count()
    }).Where(d => count % GetFactorByType(d.type) == 0).ToList();

这样,您按 type/price 组合组成的字符串进行分组,因此如果项目相等,则字符串将相等。

对于您的前三个项目示例,$"{g.Type}/{g.Price}"字符串相当于 "B/5",因此它也非常可读。

这是一个解决方法:

        //track the type:nth element discard
        var dict = new Dictionary<EnumType, int?>();
        dict[EnumType.B] = 3;
        dict[EnumType.C] = 4;

        //groupby turns our list of items into two collections, depending on whether their type is b or c
        var x = items.GroupBy(g => new { g.Type })
          .Select(g => new   //now project a new collection 
            {
                g.Key.Type,  //that has the type 
                SumPriceWithoutNthElement = //and a sum
                    //the sum is calculated by reducing the list based on index position: in where(v,i), the i is the index of the item. 
                    //We drop every Nth one, N being determined by a dictioary lookup or 2 if the lookup is null
                    //we only want list items where (index%N != N-1) is true
                    g.Where((v, i) => (i % (dict[g.Key.Type]??2)) != ((dict[g.Key.Type] ?? 2) - 1))
                    .Sum(r => r.Price) //sum the price for the remaining
            }
        ).ToList(); //tolist may not be necessary, i just wanted to look at it

我觉得你的疑问词和你的例子不一致。你说(并在代码中做了):

If the price and type are same, based on type it need to exclude every nth item from the list and then calculate the sum. i.e type B = 3, Type C = 4

这对我来说意味着您应该按类型和价格分组,所以 B/5 是一个列表,B/10 是另一个列表。但是你接着说:

Which means in above sample data, since there are 3 items each in type B once it group by price and type it need to exclude every 3rd item when calculate sum. So sum for type B will be 5+5+10+10

我不太明白这一点。对我来说 B/5 中有 3 个项目,所以 B/5 应该是 10 的总和(B/5 + B/5 + 排除)。 B/10中有3项,同样,应该是(B/10 + B/10 + 排除),总共20项。

上面的代码没有按价格分组。它输出 2 个项目的集合,Type=B,SumWithout=30 和 Type=C,SumWithout=60。这也是按价格分组,它输出 3 项集合:

        var x = items.GroupBy(g => new { g.Type, g.Price })
          .Select(g => new    
            {
                g.Key.Type,  
                g.Key.Price,
                SumPriceWithoutNthElement = 
                    g.Where((v, i) => (i % (dict[g.Key.Type]??2)) != ((dict[g.Key.Type] ?? 2) - 1))
                    .Sum(r => r.Price)                 }
        ).ToList(); 

项目是 Type=B,Price=5,SumWithout=10 和 Type=B,Price=10,SumWithout=20 和 Type=C,Price=15,SumWithout=60

也许你的意思是按类型和价格分组,删除每第 3 个项目(从 b 中删除第 4 个项目,等等),然后仅按类型再次分组,然后求和

这意味着如果您的 B 类价格是

1,1,1,1,2,2,2,2,2
    ^       ^

我们将删除一个 1 和一个 2(Ines 下面有箭头),然后总和为 9。这与删除所有类型 b 的每 3 个不同:

1,1,1,1,2,2,2,2,2
    ^     ^     ^

?

在这种情况下,也许可以再次按 Type/sum 对我第二个示例的 SumWithout 输出进行分组


我确实考虑过,如果没有 LINQ,可能会有更有效的方法来做到这一点。如果不是 LINQ,几乎肯定会更容易理解代码 - LINQ 不一定是一个神奇的灵丹妙药可以解决所有问题,尽管它看起来像一把可以解决所有问题的锤子,但有时最好避免

取决于您打算如何解决问题(价格是否是组键的一部分)构建字典并累积 0 而不是第 N 个元素的价格可能是一种方式.. 另一种方式,如果价格是关键的一部分,可以是对所有价格求和,然后从总价格

中减去(count/N)*价格