Home > Net >  How to convert nested for loop with if condition to .Net Linq?
How to convert nested for loop with if condition to .Net Linq?

Time:12-22

I am working on a function which will modify the input payload properties. The payload contains nodes and each node contains list of features. I need to remove some specific features match condition and also modify each node window property start and end time. I have written the function using traditional nested for loop, but struggling to convert it to Linq function. Anyone has idea how to convert this nested for loop function to a Linq function?

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

CodePudding user response:

Using a linq query for the second part would make things messier, better to just have a for loop

Something like this should work:

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

CodePudding user response:

Pls have look at below code snippet to avoid nested loop using 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;
            });
        }

CodePudding user response:

Let's do it in parts. This first code of yours removes features that are in a list,

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

We need to convert that to "keep features not in a list"

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

This part of your code skips if function type is a certain kind, and zeroes timespans if the function type is not a certain kind:

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

Which might be better as a loop that operates on just those things we are interested in (we want to modify only those Windows where the nodefuctionTypeId is not Hours):

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

Meaning the whole is:

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

I don't think I'd convert it all to a Linq form, as it would make a mess; linq queries should not have side effects (modify the objects the query iterates over) so particularly the second part where the time spans are being zeroed would have to become an operation where everything about the window was being copied over to a new Window if you wanted to LINQ it.

If a window is literally just a time span pair then it's not so bad, or if you want to provide a constructor that takes an existing Window and a startTime and 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);
}

Then maybe your method could become some 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();

        }

..but I don't know if I would try for a wholesale replacement of the entire nodes list, for reasons even based on the name of the method: ApplyTransformations sounds like it means "take this list of nodes and change bits of them" not "take this list of nodes and give me a new list with some or all of the nodes replaced or modified" - that sort of behavior in code could wreck something else, if the calling code is expecting a tweak and the object it sends (or objects within it) are swapped out for new ones

  • Related