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

查看:69
本文介绍了如何简化嵌套的for循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用linq简化代码.

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

我有一个包含leaveDates的列表,每个leaveDates都包含leavelist的编号.

I have a list that contains leaveDates and every leaveDates contain number of 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包含UserIdLeaveTypeStatus个字段

现在我只想计算每个状态为 1 的用户的leavedates数量,并保留类型!= 3

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

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

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

这是我的for循环代码:

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.

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

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

通过该更改,您的代码已经可以轻松读取大约100倍.

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

这些变量都不会被读取,并且它们的初始化器也没有用.删除第二个.将第一个重命名为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.

好的,我们现在有

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

在整个过程中使用helper变量:

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

我们意识到AnyFindIndex在做相同的事情.消除其中之一:

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

我们注意到我们正在最后一个if-else中复制代码.唯一的区别是days:

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

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

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

还有另外两件事:我们可以将check_halfday放入一个解释变量中,并消除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.

您有一个attendancelist突变,因此必须保持循环.但是,我们可以通过认识到内部循环中带有测试的嵌套foreach等效于:

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;

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

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

现在我们以这种非常简单的形式创建了循环,我们注意到我们可以重新排序if来简化它:

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

我们完成了.所有的计算都是由查询完成的,所有的变异都是由foreach完成的.现在,您的循环主体是一个非常清晰的条件语句.

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.

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

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.

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

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.

这篇关于如何简化嵌套的for循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆