如何简化我的嵌套for循环?[英] How can I simplify my nested for loops

本文是小编为大家收集整理的关于如何简化我的嵌套for循环?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我想使用 linq 使我的代码简短而简单.

我有一个包含 leaveDates 的列表,每个 leaveDates 都包含 leavelist 的数量.

类似这样的:

{ leaves_date = {07-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }
{ leaves_date = {08-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }
{ leaves_date = {21-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }

leaveList 包含 UserId、LeaveType、Status 字段

现在我只想计算每个状态为 1 并离开类型 != 3

的用户的 leavedates 数量

我已经尝试过使用 for 循环,但我想使用 linq.

这是我的 for 循环代码:

for (var i = 0; i < leavesresult.Count; i++) {
    for (var a = 0; a < leavesresult[i].LeaveList.Count; a++) {
        if (leavesresult[i].LeaveList[a].status == 1.ToString() && leavesresult[i].LeaveList[a].leave_type != 3.ToString()) {
            var compair1 = leavesresult[i].LeaveList[a].user_id;
            var compair2 = attendancelist.Any(z = >z.user_id == leavesresult[i].LeaveList[a].user_id);

            if (attendancelist.Any(z = >z.user_id == leavesresult[i].LeaveList[a].user_id)) {
                int index = attendancelist.FindIndex(y = >y.user_id == leavesresult[i].LeaveList[a].user_id);

                if (leavesresult[i].LeaveList[a].check_halfday == 1) {
                    attendancelist[index].days = attendancelist[index].days
                }
                else {
                    attendancelist[index].days = attendancelist[index].days + 1;
                }
            }
            else {
                if (leavesresult[i].LeaveList[a].check_halfday == 1) {
                    attendancelist.Add(new AttendanceModel {
                        user_id = leavesresult[i].LeaveList[a].user_id,
                        days = 0.5
                    });
                }
                else {
                    attendancelist.Add(new AttendanceModel {
                        user_id = leavesresult[i].LeaveList[a].user_id,
                        days = 1
                    });
                }
            }
        }
    }
}

推荐答案

我可以给你查询,但你什么也学不到.而是自己学习如何进行这种转换.诀窍是不要尝试一次完成所有操作.相反,我们进行了一系列小的、明显正确的转换,每一个都让我们更接近我们的目标.

首先将内部 for 循环重写为 foreach:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == 1.ToString() && leavelist.leave_type != 3.ToString()) 
    {
      var compair1 = leavelist.user_id;
      var compair2 = attendancelist.Any(z => z.user_id == leavelist.user_id);
      if (attendancelist.Any(z => z.user_id == leavelist.user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == leavelist.user_id);  
        if (leavelist.check_halfday == 1) 
          attendancelist[index].days = attendancelist[index].days
        else 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 1});
      }
    }
  }
}

您的代码已经通过该更改更易于阅读 100 倍.

现在我们注意到一些事情:

    if (leavelist.status == 1.ToString() && leavelist.leave_type != 3.ToString()) 

写这张支票真是太疯狂了.将其重写为合理的检查.

      var compair1 = leavelist.user_id;
      var compair2 = attendancelist.Any(z => z.user_id == leavelist.user_id);

这些变量都没有被读取过,它们的初始化器也没用.删除第二个.将第一个重命名为 user_id.

        if (leavelist.check_halfday == 1) 
          attendancelist[index].days = attendancelist[index].days
        else 
          attendancelist[index].days = attendancelist[index].days + 1;

后果毫无意义.重写这个.

好的,我们现在有

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id= leavelist.user_id;
      if (attendancelist.Any(z => z.user_id == leavelist.user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == leavelist.user_id);  
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 1});
      }
    }
  }
}

始终使用辅助变量:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      if (attendancelist.Any(z => z.user_id == user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == user_id);  
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 1});
      }
    }
  }
}

我们意识到 Any 和 FindIndex 正在做同样的事情.消除其中一个:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 1});
      }
    }
  }
}

我们注意到我们在最终的 中复制了代码.唯一的区别是 days:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        double days = leavelist.check_halfday == 1 ? 0.5 : 1;
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = days});
      }
    }
  }
}

现在您的代码比以前容易阅读 1000 倍.继续前进!将外循环重写为 foreach:

foreach (var lr in leavesresult) 
{
  foreach (var leavelist in lr.LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        double days = leavelist.check_halfday == 1 ? 0.5 : 1;
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = days});
      }
    }
  }
}

我们注意到更多的事情:我们可以将 check_halfday 放入解释变量,并消除 days.我们可以简化增量:

foreach (var lr in leavesresult) 
{
  foreach (var leavelist in lr.LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      bool halfday= leavelist.check_halfday == 1;
      if (index != -1) 
      {
        if (!halfday) 
          attendancelist[index].days += 1;
      }
      else 
      {
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = halfday ? 0.5 : 1});
      }
    }
  }
}

现在我们开始将其转换为查询.要理解的关键是突变不能进入查询.突变只进入循环,从不查询.查询提出问题,它们不执行突变.

你有一个 attendancelist 的突变,所以它必须保持在一个循环中.但是我们可以将所有查询逻辑移出循环,方法是认识到在内部循环中带有测试的嵌套 foreach 等效于:

var query = from lr in leaveresult
            from ll in lr.LeaveList
            where ll.status == "1"
            where ll.leave_type != "3"
            select ll;

非常好.现在我们可以在我们的 foreach 中使用它:

foreach(var ll in query) 
{
  var index = attendancelist.FindIndex(y => y.user_id == ll.user_id);
  var halfday = ll.check_halfday == 1;
  if (index != -1) 
  {
    if (!halfday) 
      attendancelist[index].days += 1;
  }
  else 
  {
    attendancelist.Add(
      new AttendanceModel {user_id = ll.user_id, days = halfday? 0.5 : 1 });
  }
}

现在我们有了这种极其简单形式的循环,我们注意到我们可以重新排序 if 以简化它:

foreach(var ll in query) 
{
  var index = attendancelist.FindIndex(y => y.user_id == ll.user_id);
  var halfday = ll.check_halfday == 1;
  if (index == -1) 
    attendancelist.Add(
      new AttendanceModel {user_id = ll.user_id, days = halfday? 0.5 : 1 });
  else if (!halfday) 
    attendancelist[index].days += 1;
}

我们完成了.所有的计算都是由查询完成的,所有的变化都是由 foreach 完成的,这是应该的.你的循环体现在是一个单一的、非常清晰的条件语句.

<小时>

此答案是为了回答您的问题,即如何将现有的一堆难以阅读的循环转换为易于阅读的查询.但是最好还是编写一个清楚地表达您要实现的业务逻辑的查询,我不知道那是什么.创建您的 LINQ 查询,以便轻松了解业务级别发生的情况.

在这种情况下,我怀疑您正在做的是维护每个用户的天数,以根据休假列表进行更新.那我们就这么写吧!

// dict[user_id] is the accumulated leave.
var dict = new Dictionary<int, double>();
var query = from lr in leaveresult
            from ll in lr.LeaveList
            where ll.status == "1"
            where ll.leave_type != "3"
            select ll;
foreach(var ll in query) 
{
  var halfday = ll.check_halfday == 1;
  if (!dict.ContainsKey(ll.user_id)) 
    dict[ll.user_id] = halfday? 0.5 : 1;
  else if (!halfday) 
    dict[ll.user_id] = dict[ll.user_id] + 1;
}

这似乎是一种比您经常需要搜索的列表更好的表示方式.

一旦我们在这一点上,我们就可以认识到您真正在做的是计算每个用户的总和!JamieC 的回答表明您可以使用 Aggregate 辅助方法来计算每个用户的总和.

但同样,这是基于您已经构建了整个机制来计算该总和的假设.再次重申:设计您的代码,使其清楚地按照流程的术语实现业务流程.如果你正在做的是计算那个总和,男孩,那会不会出现在你的原始代码中.努力让你的代码在做什么更清楚.

本文地址:https://www.itbaoku.cn/post/1557157.html

问题描述

I want to make my code short and simple using linq.

I have a list that contains leaveDates and every leaveDates contain number of leavelist.

Something like this:

{ leaves_date = {07-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }
{ leaves_date = {08-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }
{ leaves_date = {21-05-2018 18:30:00}, LeaveList = {System.Collections.Generic.List<TimeClock.Model.LeaveManagementModel>} }

leaveList contains UserId, LeaveType, Status fields

Now all I want is to count the number of leavedates per user who's status is 1 and leave type != 3

I have already tried using a for loop, but I want to do it with linq.

Here is my code with the for loop:

for (var i = 0; i < leavesresult.Count; i++) {
    for (var a = 0; a < leavesresult[i].LeaveList.Count; a++) {
        if (leavesresult[i].LeaveList[a].status == 1.ToString() && leavesresult[i].LeaveList[a].leave_type != 3.ToString()) {
            var compair1 = leavesresult[i].LeaveList[a].user_id;
            var compair2 = attendancelist.Any(z = >z.user_id == leavesresult[i].LeaveList[a].user_id);

            if (attendancelist.Any(z = >z.user_id == leavesresult[i].LeaveList[a].user_id)) {
                int index = attendancelist.FindIndex(y = >y.user_id == leavesresult[i].LeaveList[a].user_id);

                if (leavesresult[i].LeaveList[a].check_halfday == 1) {
                    attendancelist[index].days = attendancelist[index].days
                }
                else {
                    attendancelist[index].days = attendancelist[index].days + 1;
                }
            }
            else {
                if (leavesresult[i].LeaveList[a].check_halfday == 1) {
                    attendancelist.Add(new AttendanceModel {
                        user_id = leavesresult[i].LeaveList[a].user_id,
                        days = 0.5
                    });
                }
                else {
                    attendancelist.Add(new AttendanceModel {
                        user_id = leavesresult[i].LeaveList[a].user_id,
                        days = 1
                    });
                }
            }
        }
    }
}

推荐答案

I could give you the query and you would learn nothing. Instead learn how to do this transformation yourself. The trick is to not try to do it all at once. Rather, we make a series of small, obviously correct transformations each one of which gets us closer to our goal.

Start by rewriting the inner for loop as a foreach:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == 1.ToString() && leavelist.leave_type != 3.ToString()) 
    {
      var compair1 = leavelist.user_id;
      var compair2 = attendancelist.Any(z => z.user_id == leavelist.user_id);
      if (attendancelist.Any(z => z.user_id == leavelist.user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == leavelist.user_id);  
        if (leavelist.check_halfday == 1) 
          attendancelist[index].days = attendancelist[index].days
        else 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 1});
      }
    }
  }
}

Already your code is about 100 times easier to read with that change.

Now we notice a few things:

    if (leavelist.status == 1.ToString() && leavelist.leave_type != 3.ToString()) 

That is a crazy way to write this check. Rewrite it into a sensible check.

      var compair1 = leavelist.user_id;
      var compair2 = attendancelist.Any(z => z.user_id == leavelist.user_id);

Neither of these variables are ever read, and their initializers are useless. Delete the second one. Rename the first one to user_id.

        if (leavelist.check_halfday == 1) 
          attendancelist[index].days = attendancelist[index].days
        else 
          attendancelist[index].days = attendancelist[index].days + 1;

The consequence makes no sense. Rewrite this.

OK, we now have

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id= leavelist.user_id;
      if (attendancelist.Any(z => z.user_id == leavelist.user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == leavelist.user_id);  
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = leavelist.user_id, days = 1});
      }
    }
  }
}

Use the helper variable throughout:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      if (attendancelist.Any(z => z.user_id == user_id)) 
      {
        int index = attendancelist.FindIndex(y => y.user_id == user_id);  
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 1});
      }
    }
  }
}

We realize that the Any and the FindIndex are doing the same thing. Eliminate one of them:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        if (leavelist.check_halfday == 1) 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 0.5});
        else 
          attendancelist.Add(
            new AttendanceModel {user_id = user_id, days = 1});
      }
    }
  }
}

We notice that we are duplicating code in the final if-else. The only difference is days:

for (var i = 0; i < leavesresult.Count; i++) 
{
  foreach (var leavelist in leavesresult[i].LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        double days = leavelist.check_halfday == 1 ? 0.5 : 1;
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = days});
      }
    }
  }
}

Now your code is 1000x easier to read than it was before. Keep going! Rewrite the outer loop as a foreach:

foreach (var lr in leavesresult) 
{
  foreach (var leavelist in lr.LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      if (index != -1) 
      {
        if (leavelist.check_halfday != 1) 
          attendancelist[index].days = attendancelist[index].days + 1;
      }
      else 
      {
        double days = leavelist.check_halfday == 1 ? 0.5 : 1;
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = days});
      }
    }
  }
}

And we notice a couple more things: we can put check_halfday into an explanatory variable, and eliminate days. And we can simplify the increment:

foreach (var lr in leavesresult) 
{
  foreach (var leavelist in lr.LeaveList) 
  {
    if (leavelist.status == "1" && leavelist.leave_type != "3") 
    {
      var user_id = leavelist.user_id;
      int index = attendancelist.FindIndex(y => y.user_id == user_id);
      bool halfday= leavelist.check_halfday == 1;
      if (index != -1) 
      {
        if (!halfday) 
          attendancelist[index].days += 1;
      }
      else 
      {
        attendancelist.Add(new AttendanceModel {user_id = user_id, days = halfday ? 0.5 : 1});
      }
    }
  }
}

Now we begin transforming this to a query. The key thing to understand is that mutations must not go in queries. Mutations only go into loops, never queries. Queries ask questions, they do not perform mutations.

You have a mutation of attendancelist, so that's got to stay in a loop. But we can move all the query logic out of the loop by recognizing that the nested foreach with a test inside the inner loop is equivalent to:

var query = from lr in leaveresult
            from ll in lr.LeaveList
            where ll.status == "1"
            where ll.leave_type != "3"
            select ll;

Excellent. Now we can use that in our foreach:

foreach(var ll in query) 
{
  var index = attendancelist.FindIndex(y => y.user_id == ll.user_id);
  var halfday = ll.check_halfday == 1;
  if (index != -1) 
  {
    if (!halfday) 
      attendancelist[index].days += 1;
  }
  else 
  {
    attendancelist.Add(
      new AttendanceModel {user_id = ll.user_id, days = halfday? 0.5 : 1 });
  }
}

Now that we have the loop in this extremely simple form, we notice that we can re-order the if to simplify it:

foreach(var ll in query) 
{
  var index = attendancelist.FindIndex(y => y.user_id == ll.user_id);
  var halfday = ll.check_halfday == 1;
  if (index == -1) 
    attendancelist.Add(
      new AttendanceModel {user_id = ll.user_id, days = halfday? 0.5 : 1 });
  else if (!halfday) 
    attendancelist[index].days += 1;
}

And we're done. All the computation is done by the query, all the mutations are done by the foreach, as it should be. And your loop body is now a single, extremely clear conditional statement.


This answer is to answer your question, which was how to convert an existing bunch of hard-to-read loops into an easy-to-read query. But it would be better still to write a query that clearly expressed the business logic you're trying to implement, and I don't know what that is. Create your LINQ queries so that they make it easy to understand what is happening at the business level.

In this case what I suspect you are doing is maintaining a per-user count of days, to be updated based on the leave lists. So let's write that!

// dict[user_id] is the accumulated leave.
var dict = new Dictionary<int, double>();
var query = from lr in leaveresult
            from ll in lr.LeaveList
            where ll.status == "1"
            where ll.leave_type != "3"
            select ll;
foreach(var ll in query) 
{
  var halfday = ll.check_halfday == 1;
  if (!dict.ContainsKey(ll.user_id)) 
    dict[ll.user_id] = halfday? 0.5 : 1;
  else if (!halfday) 
    dict[ll.user_id] = dict[ll.user_id] + 1;
}

That seems like a nicer way to represent this than a list that you are constantly having to search.

Once we are at this point we can then recognize that what you are really doing is computing a per-user sum! The answer by JamieC shows that you can use the Aggregate helper method to compute a per-user sum.

But again, this is based on the assumption that you have built this whole mechanism to compute that sum. Again: design your code so that it clearly implements the business process in the jargon of that process. If what you're doing is computing that sum, boy, does that ever not show up in your original code. Strive to make it clearer what your code is doing.