将嵌套的foreach循环转换为LINQ [英] Converting nested foreach loops to 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屋!