c# - 比使用 3 个嵌套 for 循环获取我需要的数据更好的方法

标签 c# linq nested-loops

我可以用我目前正在做的方式构建字典,但我想知道是否有更好更快的方法。我正在尝试使用 LINQ SelecMany 语句但遇到了问题。

var replyChildren = reply.Children[0];
Dictionary<int, string> slowLog = new Dictionary<int, string>();
for (int i = 0; i < replyChildren.Children.Count(); i++)
{
    var ithReplyChildren = replyChildren.Children[i];
    for (int j = 0; j < 4; j++)
    {
        if (j == 3)
        {
            var jthReplyChildren = ithReplyChildren.Children[j];
            for (int k = 0; k < jthReplyChildren.Count; k++)
            {
                serverInfo += jthReplyChildren.Children[k].TextData; //+ "::";
            }
            break;
        }
        serverInfo += ithReplyChildren.Children[j].TextData + ":";
    }
    slowLog.Add(i, serverInfo);
    serverInfo = "";
}

Reply 将在内部(第 0 个元素)包含子元素,然后在其中包含子元素,依此类推。第二个 for 循环将只有 4 个子循环,而第 4 个 for 循环必须更深入一步并获取它的子循环。

最佳答案

第一步:停止使用字典。我们已经有了一个数据结构,可以将密集的、从零开始的整数范围映射到数据上:List<T> .重构:

static string GetServerInfo(Thing thing)
{
  string serverInfo = "";
  for (int j = 0; j < 4; j++)
  {
    if (j == 3)
    {
      var jthReplyChildren = thing.Children[j];
      for (int k = 0; k < jthReplyChildren.Count; k++)
      {
        serverInfo += jthReplyChildren.Children[k].TextData; //+ "::";
      }
      break;
    }
    serverInfo += thing.Children[j].TextData + ":";
  }
  return serverInfo;
}

现在你的主程序是:

List<string> slowLog = reply.Children[0].Children.Select(GetServerInfo).ToList(); 

现在我们已经消除了一个循环并将问题简化为制作GetServerInfo看起来没那么可怕。

展开外层循环:

static string GetServerInfo(Thing thing)
{
  string serverInfo = "";
  serverInfo += thing.Children[0].TextData + ":";
  serverInfo += thing.Children[1].TextData + ":";
  serverInfo += thing.Children[2].TextData + ":";
  var fourthChild = thing.Children[3];
  for (int k = 0; k < fourthChild.Count; k++)
    serverInfo += fourthChild.Children[k].TextData;
  return serverInfo;
}

太好了,现在我们只剩下一个循环了。消除那个循环:

static string GetServerInfo(Thing thing)
{
  string serverInfo = "";
  serverInfo += thing.Children[0].TextData + ":";
  serverInfo += thing.Children[1].TextData + ":";
  serverInfo += thing.Children[2].TextData + ":";
  serverInfo += string.Join("", thing.Children[3].Children.Select(x => x.TextData));
  return serverInfo;
}

我们也可以为其他人使用 Join:

static string GetServerInfo(Thing thing)
{
  string firstThree = string.Join("", thing.Children.Take(3).Select( x => x.TextData + ":"));
  string fourth = string.Join("", thing.Children[3].Children.Select(x => x.TextData));
  return firstThree + fourth;
}

进一步减少:

static string GetServerInfo(Thing thing)
{
  return 
    string.Join("", thing.Children.Take(3).Select(x => x.TextData + ":")) +
    string.Join("", thing.Children[3].Children.Select(x => x.TextData));
}

使用箭头符号:

static string GetServerInfo(Thing thing) => 
    string.Join("", thing.Children.Take(3).Select(x => x.TextData + ":")) +
    string.Join("", thing.Children[3].Children.Select(x => x.TextData));

等一下,现在我们有了一个 lambda。重写原始站点:

List<string> slowLog = reply.Children[0].Children
  .Select(thing => 
    string.Join("", thing.Children.Take(3).Select(x => x.TextData + ":")) +
    string.Join("", thing.Children[3].Children.Select(x => x.TextData)))
  .ToList(); 

我们只剩下一个声明了。 没有循环;没有循环问题。逻辑表达得非常清楚——取其中三个,将它们连接在一起,等等等等,把它变成一个列表,完成。

我们可以更进一步吗?当然!我们可以在一个帮助程序中组合选择和连接运算符,使我们的程序更流畅:

public static string SelectConcat<T>(this IEnumerable<T> items, Func<T, string> f) =>
  string.Join("", items.Select(f));

现在我们的程序变成了

List<string> slowLog = reply.Children[0].Children
  .Select(thing => 
    thing.Children.Take(3).SelectConcat(x => x.TextData + ":") +
    thing.Children[3].Children.SelectConcat(x => x.TextData))
  .ToList(); 

现在非常清楚我们在做什么。

这里的要点是:目标不是让程序变小;这不是代码高尔夫。 让代码读起来更像其预期的语义

您的原始代码使程序片段中最重要的事情看起来像是循环索引的值;查看有多少屏幕空间用于 i、j 和 k。这些是机制。编写代码以强调含义。如果你的意思是“拿前三个”,那么应该有一个 Take(3)在某处,不是for循环。

我们如何从强调 i、j 和 k 值的初始程序发展到描述数据操作的程序?看看我在那里做了什么:我做了一系列小而简单的重构,每一个都提高了抽象级别以使代码读起来更像它的预期语义或消除不必要的“仪式”。

关于c# - 比使用 3 个嵌套 for 循环获取我需要的数据更好的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45176782/

相关文章:

c# - QNetworkAccessManager->在 DLL 中调用时卡住

c# - 如何避免在服务器中自动附加 returnURL?

c# - 没有 else 的简写 If 语句

c - 如何检查整数是否是数组中元素的线性组合?

javascript - else if 的多个条件的正确代码

java - 尝试仅使用嵌套的 for 循环(Java)打印一个边上有线条的递增数字金字塔

c# - 如何从 Controller 调用我的模型?

C# 继承和 "this"关键字

sql-server - 在一对多关系中使用 EF LEFT OUTER JOIN 而不是 INNER JOIN

C# Linq 使用 distinct 和 union