比拥有3个嵌套for循环更好的方法来获取我需要的数据

时间:2017-07-18 20:48:23

标签: c# linq nested-loops

我可以用我当前正在做的方式建立一本字典,但我想知道是否有更好更快的方法。我试图使用LINQ SelecMany语句,但遇到了麻烦。

result_str = ""

def dictionary_iterator(results):
    global result_str
    for key, value in results.items():
        if isinstance(value, dict):
            result_str = result_str + key + ": \n \t"
            dictionary_iterator(value)
        else:
            result_str = result_str + key + ": " + str(value) + "\n"

    return result_str

回复会让孩子在里面(第0个元素),然后在里面会有孩子,等等。第二个for循环只有4个孩子,第四个将不得不更深入一步并得到孩子们。

3 个答案:

答案 0 :(得分:16)

第一步:停止使用字典。我们已经有了一个数据结构,它将密集的,从零开始的整数范围映射到数据: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(); 

我们归结为一个声明。 没有循环;没有循环问题。逻辑非常明确地表达 - 取其中三个,将它们连接起来,等等等等,把它变成一个列表,完成。

我们可以走得更远吗?当然!我们可以在辅助函数中组合select和concatenate运算符,使我们的程序更加流利

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值的初始程序到描述数据操作的程序?看看我在那里做了什么:我做了一系列小的,简单的重构,每个提高了抽象级别,使代码读取更像其预期的语义或消除不必要的“仪式”。

答案 1 :(得分:1)

我不是靠近IDE来测试它,但是我把它缩小了一点:

var replyChildren = reply.Children[0];
Dictionary<int, string> slowLog = new Dictionary<int, string>();

int i = 0;
foreach (var child in replyChildren.Children)
{
    StringBuilder sb = new StringBuilder();

    sb.AppendFormat("{0}:", child.Children[0].TextData);
    sb.AppendFormat("{0}:", child.Children[1].TextData);
    sb.AppendFormat("{0}:", child.Children[2].TextData);

    sb.Append(string.Join(string.Empty, child.Children[3].Select(x => x.TextData));

    slowLog.Add(i, sb.ToString());
    i++;
}

我不完全了解您的要求,但正如评论中提到的@EricLippert,您可以使用List<string> ...

答案 2 :(得分:0)

第一步,让我们展开中间循环:

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];
    serverInfo += ithReplyChildren.Children[0].TextData + ":";
    serverInfo += ithReplyChildren.Children[1].TextData + ":";
    serverInfo += ithReplyChildren.Children[2].TextData + ":";
    var jthReplyChildren = ithReplyChildren.Children[3];
    for (int k = 0; k < jthReplyChildren.Count; k++)
    {
        serverInfo += jthReplyChildren.Children[k].TextData;//+ "::";
    }

    slowLog.Add(i, serverInfo);
    serverInfo = "";
}

然后我们将for()替换为foreach(()

var replyChildren = reply.Children[0];
var slowLog = new Dictionary<int, string>();
int i = 0;

foreach(var ithReplyChildren in replyChildren.Children)
{
    serverInfo += ithReplyChildren.Children[0].TextData + ":";
    serverInfo += ithReplyChildren.Children[1].TextData + ":";
    serverInfo += ithReplyChildren.Children[2].TextData + ":";

    var jthReplyChildren = ithReplyChildren.Children[3];
    foreach (var kthReplyChildren in jthReplyChildren.Children)
    {
        serverInfo += kthReplyChildren.TextData;//+ "::";
    }

    slowLog.Add(i++, serverInfo);
    serverInfo = "";
}

然后迈克建议使用StringBuilder

var replyChildren = reply.Children[0];
var slowLog = new Dictionary<int, string>();
int i = 0;

foreach (var ithReplyChildren in replyChildren.Children)
{
    var serverInfo = new StringBuilder();
    serverInfo.Append(ithReplyChildren.Children[0].TextData);
    serverInfo.Append(':');

    serverInfo.Append(ithReplyChildren.Children[1].TextData);
    serverInfo.Append(':');
    serverInfo.Append(ithReplyChildren.Children[2].TextData);
    serverInfo.Append(':');

    var jthReplyChildren = ithReplyChildren.Children[3];
    foreach (var kthReplyChildren in jthReplyChildren.Children)
    {
        serverInfo.Append(kthReplyChildren.TextData);
    }

    slowLog.Add(i++, serverInfo.ToString());
}