分组和单独列表

时间:2019-01-29 17:50:38

标签: c# .net linq .net-4.6.1

我具有以下实体结构

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();

2 个答案:

答案 0 :(得分:1)

这是一个解决方法:

        //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

在我看来,您的疑问词和您的示例不符。您说过(并在代码中做了):

  

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

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

  

在上述示例数据中,这意味着B型中一旦有3个商品按价格分组并按价格分组,则在计算总和时需要排除每3个商品。所以类型B的总和是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中删除,从c中删除第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
    ^     ^     ^

在这种情况下,也许再按类型/总和分组第二个示例中的SumWithout输出


我确实认为,如果没有LINQ,可能会有更有效的方法来执行此操作。如果非LINQ,几乎可以肯定会更容易理解代码-LINQ不一定是可以杀死的绝妙魔术所有的问题,即使看起来像锤子,也可以解决所有问题,有时还是要避免

取决于您打算解决问题的方式(是否价格是组密钥的一部分),构建字典并每N个元素累积0而不是价格,这可能是一种方法。另一种方法,如果price是成为关键的一部分,可以是对所有价格求和,然后从总价格中减去(count / N)* price

答案 1 :(得分:0)

通过始终是唯一的新对象进行分组,可以确保您拥有与项目一样多的组。尝试这样的事情:

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();

这样,您将由类型/价格组合组成的字符串分组,因此,如果项目相等,则字符串将相等。

在您的前三个示例中,$"{g.Type}/{g.Price}"字符串等于"B/5",因此也很容易理解。