将嵌套的foreach循环转换为LINQ [英] Converting nested foreach loops to LINQ

查看:169
本文介绍了将嵌套的foreach循环转换为LINQ的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了下面的代码来设置各种类的属性。它的工作原理,但我新的一年的解决方案之一是尽可能多地使用LINQ,显然这个代码没有。有没有办法以纯LINQ格式重写它,最好是不使用 foreach 循环? (甚至更好,如果可以在一个单一的LINQ语句中完成 - 子状态是好的。)

我试着玩 join 但是这并没有给我任何地方,因此我要求回答这个问题 - 最好没有解释,因为我宁愿反编译的解决方案,弄清楚它是如何工作的。 (正如你大概猜到我现在阅读LINQ比写更好,但我打算改变这一点...)

  public void PopulateBlueprints(IEnumerable< Blueprint>蓝图)
{
XElement items = GetItems();
// item id =>名称映射
var itemsDictionary =(
从项目中的
选择新
{
Id = Convert.ToUInt32(item.Attribute(id)。Value) ,
Name = item.Attribute(name)。Value,
})。Distinct()。ToDictionary(pair => pair.Id,pair => pair.Name);

foreach(蓝图中的蓝图)
{
foreach(blueprint.Input.Keys中的var材质)
{
if(itemsDictionary.ContainsKey material.Id))
{
material.Name = itemsDictionary [material.Id];
}
else
{
Console.WriteLine(m:+ material.Id);



if(itemsDictionary.ContainsKey(blueprint.Output.Id))
{
blueprint.Output.Name = itemsDictionary [blueprint。 Output.Id];
}
else
{
Console.WriteLine(b:+ blueprint.Output.Id);





必要类的定义如下;他们只是数据的容器,我已经剥离了所有与我的问题无关的部分:

  public class Material 
{
public uint Id {get;组; }

public string Name {get;组; }
}

public class Product
{
public uint Id {get;组; }

public string Name {get;组; }
}

public class Blueprint
{
public IDictionary< material,uint>输入{get;组; }

public Product Output {get;组; }
}


解决方案

这实际上是转换为LINQ的一个很好的选择 - 至少不是现在的形式。

是的,你有一个嵌套的foreach循环 - 但是你正在做其他的事情在顶层的foreach循环中,所以它不是只包含嵌套的易于转换的表单。更重要的是,body的代码都是关于副作用的,无论是写入控制台还是更改所找到的对象中的值。当你有一个复杂的查询时,LINQ是很好的,你想循环遍历它,依次操作每个项目,可能带有副作用...但是你的查询不是真的很复杂,所以你不会得到太多的好处。



你可以做的一件事就是给 Blueprint Product 一个包含 Id 名称 。然后你可以编写一个方法来通过 itemsDictionary 根据每个查询来更新产品和蓝图:

  UpdateNames(itemsDictionary,blueprints); 
UpdateNames(itemsDictionary,blueprints.SelectMany(x => x.Input.Keys));


$ b private static void UpdateNames< TSource>(其中,TSource:INameAndId $(字符串,字符串> idMap,
IEnumerable< TSource> source) b $ b {
foreach(源文件中的源文件)
{
字符串名称;
if(idMap.TryGetValue(item.Id,out name))
{
item.Name = name;





这是假设你不'实际上需要控制台输出。如果你这样做,你总是可以传入适当的前缀,并在方法中添加一个else块。请注意,我已经使用 TryGetValue ,而不是在每次迭代的字典上执行两次查找。


I've written the following code to set the properties on various classes. It works, but one of my new year's rsolutions is to make as much use of LINQ as possible and obviously this code doesn't. Is there a way to rewrite it in a "pure LINQ" format, preferably without using the foreach loops? (Even better if it can be done in a single LINQ statement - substatements are fine.)

I tried playing around with join but that didn't get me anywhere, hence I'm asking for an answer to this question - preferably without an explanation, as I'd prefer to "decompile" the solution to figure out how it works. (As you can probably guess I'm currently a lot better at reading LINQ than writing it, but I intend to change that...)

 public void PopulateBlueprints(IEnumerable<Blueprint> blueprints)
 {
   XElement items = GetItems();
   // item id => name mappings
   var itemsDictionary = (
     from item in items
     select new
     {
       Id = Convert.ToUInt32(item.Attribute("id").Value),
       Name = item.Attribute("name").Value,
     }).Distinct().ToDictionary(pair => pair.Id, pair => pair.Name);

  foreach (var blueprint in blueprints)
  {
    foreach (var material in blueprint.Input.Keys)
    {
      if (itemsDictionary.ContainsKey(material.Id))
      {
        material.Name = itemsDictionary[material.Id];
      }
      else
      {
        Console.WriteLine("m: " + material.Id);
      }
    }

    if (itemsDictionary.ContainsKey(blueprint.Output.Id))
    {
      blueprint.Output.Name = itemsDictionary[blueprint.Output.Id];
    }
    else
    {
      Console.WriteLine("b: " + blueprint.Output.Id);
    }
  }
}

Definition of the requisite classes follow; they are merely containers for data and I've stripped out all the bits irrelevant to my question:

public class Material
{
  public uint Id { get; set; }

  public string Name { get; set; }
}

public class Product
{
  public uint Id { get; set; }

  public string Name { get; set; }
}

public class Blueprint
{
  public IDictionary<Material, uint> Input { get; set; }

  public Product Output { get; set; }
}

解决方案

I don't think this is actually a good candidate for conversion to LINQ - at least not in its current form.

Yes, you have a nested foreach loop - but you're doing something else in the top-level foreach loop, so it's not the easy-to-convert form which just contains nesting.

More importantly, the body of your code is all about side-effects, whether that's writing to the console or changing the values within the objects you've found. LINQ is great when you've got a complicated query and you want to loop over that to act on each item in turn, possibly with side-effects... but your queries aren't really complicated, so you wouldn't get much benefit.

One thing you could do is give Blueprint and Product a common interface containing Id and Name. Then you could write a single method to update the products and blueprints via itemsDictionary based on a query for each:

UpdateNames(itemsDictionary, blueprints);
UpdateNames(itemsDictionary, blueprints.SelectMany(x => x.Input.Keys));

...

private static void UpdateNames<TSource>(Dictionary<string, string> idMap,
    IEnumerable<TSource> source) where TSource : INameAndId
{
    foreach (TSource item in source)
    {
        string name;
        if (idMap.TryGetValue(item.Id, out name))
        {
            item.Name = name;
        }
    }
}

This is assuming you don't actually need the console output. If you do, you could always pass in the appropriate prefix and add an "else" block in the method. Note that I've used TryGetValue instead of performing two lookups on the dictionary for each iteration.

这篇关于将嵌套的foreach循环转换为LINQ的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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