【问题标题】:如何将带有if条件的嵌套for循环转换为.Net Linq?
【发布时间】:2022-01-23 09:36:48
【问题描述】:

我正在开发一个可以修改输入负载属性的函数。有效负载包含节点,每个节点都包含特征列表。我需要删除一些特定的功能匹配条件,并修改每个节点窗口属性的开始和结束时间。我已经使用传统的嵌套 for 循环编写了该函数,但很难将其转换为 Linq 函数。有人知道如何将此嵌套的 for 循环函数转换为 Linq 函数吗?

private void ApplyTransformation(InputPayload payload, int startTime = 8, int endTime = 15) 
        {
            var nodes = payload.Nodes;
            for (var i = 0; i < nodes.Count(); ++i)
            {
                var node = nodes[i];
                var features = node.Features;
                for (var j = 0; j < features.Count(); ++j)
                {
                    var feature = features[j];
                    if (feature.NodeFeatureTypeID 
                        == FeatureTypeEnum.FEATURE_A
                        || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_B
                        || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_C
                        || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_D
                    )
                    {
                        features.RemoveAt(j);
                    }
                }
                var windows = node.Windows;
                for (var k = 0; k < windows.Count(); ++k)
                {
                    var window = windows[k];
                    if (window.NodeFunctionTypeID == FeatureTypeEnum.MM_HOURS) continue;
                    window.StartHour = new TimeSpan(startTime, 0, 0);
                    window.EndHour = new TimeSpan(endTime, 0, 0);
                }
            }
        } 

【问题讨论】:

  • LINQ 在您将数据结构视为不可变时工作得更好,因此请考虑使用 Where() 过滤列表删除元素并使用 Select() 将元素转换为新元素,而不是删除和添加.然后用转换后的版本替换原始列表。

标签: c# .net


【解决方案1】:

让我们分部分进行。您的第一个代码删除列表中的功能,

            for (var j = 0; j < features.Count(); ++j)
            {
                var feature = features[j];
                if (feature.NodeFeatureTypeID 
                    == FeatureTypeEnum.FEATURE_A
                    || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_B
                    || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_C
                    || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_D
                )
                {
                    features.RemoveAt(j);
                }
            }

我们需要将其转换为“保留不在列表中的功能”

var discard = new [] {FeatureTypeEnum.FEATURE_A, FeatureTypeEnum.FEATURE_B, FeatureTypeEnum.FEATURE_C, FeatureTypeEnum.FEATURE_D };

node.Features = node.Features.Where(f => !discard.Contains(f)).ToArray();

如果函数类型是某种类型,这部分代码会跳过,如果函数类型不是某种类型,则将时间跨度归零:

if (window.NodeFunctionTypeID == FeatureTypeEnum.MM_HOURS) continue;
                window.StartHour = new TimeSpan(startTime, 0, 0);
                window.EndHour = new TimeSpan(endTime, 0, 0);

作为一个仅在我们感兴趣的事物上运行的循环可能会更好(我们只想修改 nodefctionTypeId 不是 Hours 的那些 Windows):

foreach(var window in node.Windows.Where(w => w.NodeFunctionTypeID != FeatureTypeEnum.MM_HOURS){
  window.StartHour = new TimeSpan.FromHous(startTime);
  window.EndHour = new TimeSpan.FromHours(endTime);
}
  

意思是:

    private void ApplyTransformation(InputPayload payload, int startTime = 8, int endTime = 15) 
    {
        var discard = new [] {FeatureTypeEnum.FEATURE_A, FeatureTypeEnum.FEATURE_B, FeatureTypeEnum.FEATURE_C, FeatureTypeEnum.FEATURE_D };

        foreach (var node in payload.Nodes)
        {
            node.Features = node.Features.Where(f => !discard.Contains(f)).ToArray();

            foreach(var window in node.Windows.Where(w => w.NodeFunctionTypeID != FeatureTypeEnum.MM_HOURS){
                window.StartHour = TimeSpan.FromHous(startTime);
                window.EndHour = TimeSpan.FromHours(endTime);
            }
        }
    } 

我不认为我会将它全部转换为 Linq 表单,因为它会弄得一团糟; linq 查询不应该有副作用(修改查询迭代的对象),所以特别是时间跨度归零的第二部分必须成为一个操作,如果你想要,关于窗口的所有内容都被复制到一个新窗口对其进行 LINQ。

如果一个窗口实际上只是一个时间跨度对,那么它还不错,或者如果你想提供一个构造函数,它接受一个现有的 Window 以及一个 startTime 和 endTime:

public Window(Window copyFrom, int startTime, int endTime){
  this.X = copyFrom.X;
  this.Y = copyFrom.Y;
  ...
  this.StartHour = TimeSpan.FromHours(strtTime);
  this.EndHour = TimeSpan.FromHours(endTime);
}

那么也许你的方法可以变成一些 linq:

        foreach (var node in payload.Nodes)
        {
            node.Features = node.Features.Where(f => !discard.Contains(f)).ToArray();

            node.Windows = node.Windows.Select(w => w.NodeFunctionTypeID == FeatureTypeEnum.MM_HOURS ? w : new Window(w, startTime, endTime).ToArray();

        }

..但我不知道我是否会尝试批量替换整个节点列表,原因甚至基于方法的名称:ApplyTransformations 听起来意味着“获取此节点列表并更改位其中”而不是“获取此节点列表并给我一个新列表,其中部分或全部节点已替换或修改”-如果调用代码期望调整和对象,代码中的这种行为可能会破坏其他东西它发送的(或其中的对象)被替换为新的

【讨论】:

    【解决方案2】:

    在第二部分使用 linq 查询会使事情变得更混乱,最好只使用 for 循环

    这样的事情应该可以工作:

        var nodes = payload.Nodes;
        nodes.Features = nodes.Features.Where(f => !(
            f.NodeFeatureTypeID == FeatureTypeEnum.FEATURE_A
            || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_B
            || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_C
            || feature.FeatureTypeID == FeatureTypeEnum.FEATURE_D
            )
        );
        foreach(var window in nodes.Windows)
        {
            if (window.NodeFunctionTypeID != FeatureTypeEnum.MM_HOURS) 
            {
                window.StartHour = new TimeSpan(startTime, 0, 0);
                window.EndHour = new TimeSpan(endTime, 0, 0);
            }
        }
    

    【讨论】:

      【解决方案3】:

      请查看下面的代码 sn-p 以避免使用 linq 的嵌套循环。

         public class Employee
              {
                  public string Name { get; set; }
                  public List<EmployeeProject> EmployeeProject { get; set; }
      
              }
              public class EmployeeProject
              {
                  public string ProjectName { get; set; }
                  public string ClientName { get; set; }
              }
      
              /// <summary>
              /// Object mapping
              /// </summary>
              /// <returns></returns>
              public static List<Employee> CreateObject()
              {
                  var employeeList = new List<Employee>();
      
                  var employee = new Employee();
                  var employeeProjectList = new List<EmployeeProject>();
      
                  employee.Name = "John";
      
                  var employeeProject = new EmployeeProject();
                  employeeProject.ProjectName = "Chrome";
                  employeeProject.ClientName = "Google";
                  employeeProjectList.Add(employeeProject);
      
      
                  employeeProject = new EmployeeProject();
                  employeeProject.ProjectName = "WhatsApp";
                  employeeProject.ClientName = "Meta";
                  employeeProjectList.Add(employeeProject);
                  employee.EmployeeProject = employeeProjectList;
                  employeeList.Add(employee);
      
      
                  employee.Name = "Alex";
                  employeeProjectList = new List<EmployeeProject>();
                  employeeProject = new EmployeeProject();
                  employeeProject.ProjectName = "Chrome2";
                  employeeProject.ClientName = "Google2";
                  employeeProjectList.Add(employeeProject);
      
                  employeeProject = new EmployeeProject();
                  employeeProject.ProjectName = "WhatsApp2";
                  employeeProject.ClientName = "Meta2";
                  employeeProjectList.Add(employeeProject);
                  employee.EmployeeProject = employeeProjectList;
                  employeeList.Add(employee);
      
                  return employeeList;
              }
      
              /// <summary>
              /// Linq function 
              /// </summary>
              public static void LinqFunctionForNestedQuery()
              {
                  var employeeObject = CreateObject();
      
                  var result1 = employeeObject.Select(x =>
                  {
                      x.EmployeeProject = x.EmployeeProject.Select(y =>
                      {
                          y.ProjectName.Contains("Chrome");
                          return y;
                      }).ToList();
                      return x;
                  });
              }
      
      

      【讨论】:

        【解决方案4】:

        为了保持代码的可读性并使其更简单,我修改了代码。

        第 1 步:如果可以的话,用 foreach 替换 for

        第 2 步: 将 foreach 替换为 linq 重要提示。 Resharper 可帮助您提供代码建议。

        解决方案

        private void ApplyTransformation(InputPayload payload, int startTime = 8, int endTime = 15)
            {
                foreach (var node in payload.Nodes)
                {
                    var features = node.Features;
                    foreach (var feature in features.Where(feature => feature.NodeFeatureTypeID == FeatureTypeEnum.FEATURE_A ||
                                                                      feature.FeatureTypeID == FeatureTypeEnum.FEATURE_B ||
                                                                      feature.FeatureTypeID == FeatureTypeEnum.FEATURE_C ||
                                                                      feature.FeatureTypeID == FeatureTypeEnum.FEATURE_D))
                    {
                        features.Remove(feature);
                    }
        
                    
                    var windows = node.Windows;
                    foreach (var window in windows
                                 .Where(window => window.NodeFunctionTypeID != FeatureTypeEnum.MM_HOURS))
                    {
                        window.StartHour = new TimeSpan(startTime, 0, 0);
                        window.EndHour = new TimeSpan(endTime, 0, 0);
                    }
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-12-06
          • 2013-03-05
          • 2020-03-06
          • 1970-01-01
          • 2018-07-08
          • 1970-01-01
          • 2023-02-15
          相关资源
          最近更新 更多