【问题标题】:GroupBy in LINQ and set propertyLINQ 中的 GroupBy 并设置属性
【发布时间】:2011-11-25 18:46:10
【问题描述】:

我有这样的课:

public class Foo
{
  public string Regn{get;set;}

  public string DocName{get;set;}

  ...
}

在我的应用程序中,这个类与 IEnumerable 一起使用:

IEnumerable<Foo> items;

如何获取新的 IEnumerable,对于具有相同 Regn 和 DocName 属性的所有项目 DocName 设置如下(仅当具有相同 DocName >1 的对象时):

item.DocName=item.DocName+".1";//+"2",etc.

[更新] 输入样本:

Regn DocName
1    1
1    2
1    2
2    5
2    5
2    6

输出:

Regn DocName
1    1
1    2.1
1    2.2
2    5.1
2    5.2
2    6

【问题讨论】:

  • 很抱歉,您能稍微澄清一下您的问题吗?我不确定你想要达到什么目标。也许添加一些伪代码?
  • @Dublicator,为方法提供样本输入和输出。
  • @Dublicator,我更新了我的答案。

标签: c# linq group-by


【解决方案1】:

如果您有 Foo 的默认构造函数,请尝试使用:

var newItems = items.
            GroupBy(f => Tuple.Create(f.Regn, f.DocName)).
            SelectMany(gr => gr.Count()<=1 ? gr : gr.Select((f, i) => new Foo
            {
                Regn = f.Regn,
                DocName = f.DocName + "." + (i + 1)
            }));

【讨论】:

    【解决方案2】:

    您可以使用 LINQ 进行分组并丢弃只有一个项目的组,然后遍历每个组中的项目以设置 DocName

    // Group and filter
    var groups = items.GroupBy(i => new { i.Regn, i.DocName })
                      .Where(g => g.Count() > 1);
    
    // Iterate over each group with many items
    foreach (var g in groups) {
        var itemsInGroup = g.ToArray();
        // Iterate over the items and set DocName
        for (var i = 0; i < itemsInGroup.Length; ++i) {
            itemsInGroup[i].DocName = g.Key + "." + (i + 1);
        }
    }
    

    【讨论】:

      【解决方案3】:

      一切尽在一份声明中,只是为了好玩。

      var query = items.GroupBy(i => new { i.DocName, i.Regn })
          .SelectMany(group => 
              {
                  int itemNum = 0;
                  return group.Select(item =>
                      {
                          var suffix = itemNum > 0 ? ("." + itemNum) : "";
                          var newDocName = item.DocName + suffix;
                          itemNum++;
                          return new { item, NewDocName = newDocName };
                      });
              });
      

      【讨论】:

        【解决方案4】:

        或者使用 LINQ 语句创建一个新的结果集,例如:

        var fixedSet = from entry in existing
                       group entry by entry.DocName + entry.Regn into groupedEntries
                       let groupedEntriesAsArray = groupedEntries.ToArray()
                       from groupedEntry in groupedEntriesAsArray
                       let index = Array.IndexOf(groupedEntriesAsArray, groupedEntry)
                       select new Foo
                       {
                           DocName =
                               string.Format("{0}.{1}", groupedEntry.DocName, index + 1),
                           Regn =
                               string.Format("{0}.{1}", groupedEntry.Regn, index + 1)
                       };
        

        【讨论】:

        • 我不是反对者,但您的代码中存在一些问题:1. 不可读,2. 您可以将 Select 与索引器一起使用,而不是 Array.IndexOf(groupedEntriesAsArray, groupedEntry),您当前的方法在以下情况下不好性能问题也很重要,它取决于类的平等实现,可能导致不同对象的相同值。在这种情况下,我认为 MAKKAM 答案是最好的答案。
        • @Saeed Amiri:谢谢。我知道使用这种方法的负面属性,但我不得不不同意可读性。我给出了这个例子,因为没有其他答案引入了像 linq 语句这样的查询语法。我阅读它的方式,它是完美的,并且是最易读的(与其他人相比)。但这当然是个人喜好
        • 正如你所说,这似乎是个人喜好,但在所有 dot Notation 中更喜欢在 linq 中查询语法,但在某些情况下,请参阅stackoverflow.com/questions/630045/… 进行讨论。
        • @Polity,你的代码可读性很强,但我完全同意 Saeed 关于性能问题的看法。此外,我认为通过 Regn 和 DocName 的串联进行分组并不是一个好主意。查看 foo1 = new Foo{Regn="c", DocName="ab"} 和 foo2 = new Foo{Regn="bc", DocName="a"}。您的查询会将它们放在一个组中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-17
        • 2017-02-08
        相关资源
        最近更新 更多