【问题标题】:ForEach for list of list in LinqForEach 用于 Linq 中的列表列表
【发布时间】:2016-03-14 09:59:03
【问题描述】:

我正在尝试对列表列表进行一些操作。

我有一个包含一些属性的列表。我已根据 GroupId 属性将列表拆分为子列表。

我将列表中的每个列表项分配给另一个类。但在这里我不得不使用 2 个 ForEachs。有什么方法可以做到这一点,以便在输入列表中超过 10000 个列表项的大量输入时提高性能。下面是我试过的代码

 class Comp
 {
    public int CompId { get; set; }
    public string CompName { get; set; }
    public int GroupId { get; set; }
 }

 class EmpComp
 {
        public int EmpCompId { get; set; }
        public string EmpCompName { get; set; }
        public int EmpId { get; set; }
        public int GroupId { get; set; }
 }

    #region Input

    List<Comp> compList = new List<Comp>();
    compList.Add(new Comp { CompId = 1, CompName = "One", GroupId = 1 });
    compList.Add(new Comp { CompId = 2, CompName = "Two", GroupId = 1 });
    compList.Add(new Comp { CompId = 3, CompName = "One", GroupId = 2 });
    compList.Add(new Comp { CompId = 4, CompName = "Three", GroupId = 1 });
    compList.Add(new Comp { CompId = 5, CompName = "One", GroupId = 4 });
    compList.Add(new Comp { CompId = 6, CompName = "Two", GroupId = 4 }); 

    #endregion

    var groupedCompList = compList.GroupBy(u => u.GroupId ).Select(grp => grp.ToList()).ToList();
    List<EmpComp> empCompList = new List<EmpComp>();
    int empId = 0;//Just for reference
    groupedCompList.ForEach(x =>
    {
        x.ForEach(y =>
        {
            EmpComp empComp = new EmpComp();
            empComp.EmpCompId = y.CompId;
            empComp.EmpCompName = y.CompName;
            empComp.GroupId = y.GroupId ;
            empComp.EmpId = empId + 1;
            empCompList.Add(empComp);
        });
        empId++;
    });

我想避免在这里使用两个 ForEach。

注意:我还有一些其他的 id 和字符串需要在 GroupId 中分配。 empId 只是一个例子

【问题讨论】:

  • ForEachs 对性能的影响可以忽略不计。无论您使用哪种编码方式,您仍然需要进行groupedCompList.Count * x.Count 操作。
  • 没有 ToList 它工作得更快... List empCompList = new List(); compList.GroupBy(u => u.DisplayOrder).ForEach(x => x.ForEach(y =>...));
  • 正如@Rob 所说,您将不得不遍历这两个列表 - 因为总是如此,您为什么不想拥有两个 foreach?对于下一个开发人员来说,其他任何东西都会变得脆弱且难以阅读:-)

标签: c# linq list foreach


【解决方案1】:

不需要嵌套的ForEachs 甚至SelectMany,因为创建的组只是被嵌套的ForEachs 扁平化了。事实上,当前代码是不正确的,因为当调用 GroupBy 时,DisplayOrder 属性并没有真正排序——只是碰巧每个不同 DisplayOrder 的第一个值在源数据中是有序的。如果不是这样,结果就会乱七八糟。

这可以在没有任何分组或使用ForEach 的情况下工作,并且它正确地对结果进行排序:

List<EmpComp> empCompList =
    compList
        .OrderBy(u => u.DisplayOrder)
        .Select((y, n) => new EmpComp()
        {
            EmpCompId = y.CompId,
            EmpCompName = y.CompName,
            DisplayOrder = y.DisplayOrder,
            EmpId = n + 1,
        }).ToList();

感谢 cmets,我意识到原始代码在内循环之外有 empId++;

这是我解决问题的代码:

List<EmpComp> empCompList =
    compList
        .OrderBy(u => u.DisplayOrder)
        .GroupBy(u => u.DisplayOrder)
        .Select((ys, n) =>
            ys
                .Select(y => new EmpComp()
                {
                    EmpCompId = y.CompId,
                    EmpCompName = y.CompName,
                    DisplayOrder = y.DisplayOrder,
                    EmpId = n + 1,
                }))
        .SelectMany(x => x)
        .ToList();

这给出了这个结果:

【讨论】:

  • 但 OP 代码似乎为每个 DisplayOrder 组键分配了一个唯一的 EmpId
  • 没错.. 对于每个 DisplayOrder,EmpId 都不同。
  • 代码中声明的empId仅供参考。这可能是另一个对象或其他任何东西的属性
  • DisplayOrder 只是一个用于对列表进行分组的属性
  • @Midhun - 但是你对那个分组什么也没做。
【解决方案2】:

这样怎么样(调整 Enigmativity 的答案):

int empId=0;
Dictionary<string, int> displayOrderTable = compList.GroupBy(u => u.DisplayOrder).Distinct().ToDictionary(x=>x.Key, (v,k)=>empId++;)
List<EmpComp> empCompList =
    compList
        .OrderBy(u => u.DisplayOrder)
        .Select((y, n) => new EmpComp()
        {
            EmpCompId = y.CompId,
            EmpCompName = y.CompName,
            DisplayOrder = y.DisplayOrder,
            EmpId = displayOrderTable[y.DisplayOrder],
        }).ToList();

这将在 O(N+N) = O(N) 时间内运行,而不是 O(N^2)(使用 for in for)。

【讨论】:

    【解决方案3】:

    这是您仅使用 LINQ 的等效代码

    var empCompList = compList
        .GroupBy(c => c.GroupId)
        .SelectMany((g, i) => g.Select(c => new EmpComp
        {
            EmpCompId = c.CompId,
            EmpCompName = c.CompName,
            GroupId = c.GroupId,
            EmpId = i + 1
        }))
        .ToList();
    

    关键是使用SelectMany overload接收源元素和索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多