根据分组修改列表中元素属性的最佳方法

时间:2011-11-19 20:02:53

标签: c# linq collections

我有一个List CartItem,其中一些具有相同的Name属性。 我需要所有Name属性都是唯一的。显而易见的事情是将它们组合成具有Quantity属性>的单个项目。 1,但由于我不会进入的原因,我不能自由地这样做。我也不想通过在每个名称附加一个GUID来做丑陋,简单的事情。所以我想在Name属性中附加一个像“(1/3)”这样的子字符串,但仅限于具有重复项的名称。已经是唯一的名字我想单独​​留下,所以没有人说“(1 of 1)”。

这是我正在谈论的单元测试。我正试图想出一个更好的方法来评论评论之间的界限:

class CartItem
{
   public string Name { get; set; }
   public int Quantity { get; set; }
   public decimal UnitPrice { get; set; }
}

[TestMethod]
public void TestSOQuestion()
{
   var exampleList = new List<CartItem>
   {
      new CartItem 
         { Name="Same String", UnitPrice=10.00m, Quantity=1},
      new CartItem 
         { Name="Same String", UnitPrice=10.00m, Quantity=1},
      new CartItem 
         { Name="Other String", UnitPrice=14.99m, Quantity=1},
      new CartItem 
         { Name="Other String", UnitPrice=14.99m, Quantity=1},
      new CartItem 
         { Name="Other String", UnitPrice=14.99m, Quantity=1},
      new CartItem 
         { Name="Only One Of This", UnitPrice=29.99m, Quantity=1}
   };

var expectedList = new List<CartItem>
   {
      new CartItem 
        { Name="Same String (1 of 2)", UnitPrice=10.00m, Quantity=1},
      new CartItem 
        { Name="Same String (2 of 2)", UnitPrice=10.00m, Quantity=1},
      new CartItem 
        { Name="Other String (1 of 3)", UnitPrice=14.99m, Quantity=1},
      new CartItem 
        { Name="Other String (2 of 3)", UnitPrice=14.99m, Quantity=1},
      new CartItem 
        { Name="Other String (3 of 3)", UnitPrice=14.99m, Quantity=1},
      new CartItem 
        { Name="Only One Of This", UnitPrice=29.99m, Quantity=1}
   };

        //This is the best I can come up with:
        var groups = exampleList.GroupBy(item => item.Name)
                     .Where(g => g.Count() > 1);
        foreach (var group in groups)
        {
            int i = 1;
            foreach (var item in group)
            {
                item.Name = item.Name + string.Format(
                               " ({0} of {1})", i++, group.Count());
            }
        }
        //Is there a better way to do this?

        for (int i = 0; i < expectedList.Count(); i++)
        {
            Assert.AreEqual(expectedList[i].Name, exampleList[i].Name);
        }
    }

2 个答案:

答案 0 :(得分:2)

var result=exampleList.GroupBy(p=>p.Name).SelectMany(p=>p.Select((value,index)=> new CartItem()
            {
                Name = value.Name +
                       (p.Count() == 1?"": string.Format(" ({0} of {1})", index+1, p.Count())),
                UnitPrice=value.UnitPrice,
                Quantity=value.Quantity,
            }
            )).ToList();

答案 1 :(得分:0)

感谢RedHat的深思熟虑的答案。

以下是我最终使用的内容:

exampleList.GroupBy(item => item.Name)
    .Where(g => g.Count() > 1).ToList().ForEach
(
    group =>
    {
        foreach (var item in group
            .Select((x, i) => new { val = x, idx = i + 1 }))
        {
            item.val.Name += string.Format
            (
                " ({0} of {1})", item.idx, group.Count()
            );
        }
    }
);

我更喜欢将结果投射到新的List中,而不是将其投影到新的Name中 有选择地修改现有集合中的Name属性。 由于实际数据集主要具有非重复Where(g => g.Count() > 1)属性 哪个会被Cart排除在外, 通过嵌套的foreach循环进行的迭代次数会减少。 而且,这个Dictionary类被大大简化了 题;它实际上包含更多属性,包括a Cart个名称 - 值对。必须投射每个房产 结果将意味着与string Name类更紧密的耦合。 如果我们将来要添加或修改属性,请使用此代码 也必须更新。 为了避免这种维护问题,这种方法只需要 存在{{1}}属性。