如何使用 LINQ 避免嵌套循环?

发布于 2024-12-18 16:33:17 字数 323 浏览 4 评论 0原文

我一直在阅读有关 LINQ to Objects 的内容,现在我的同事希望我向他们介绍它。

现在,我对运算符和语法选择有了一定的了解,但我听说您可以通过使用 LINQ 来避免大量嵌套循环。不过,我很难想出一组好的“之前和之后”代码清单来证明这一点。

我在 Magennis 的书中找到了使用和不使用 LINQ 进行排序和分组的一个很好的示例,他还提供了一个编写 xml 的示例。但是那些嵌套循环呢?考虑到我们通常需要一两个 foreach 循环来迭代查询结果,这是否是一个现实的主张?

如果有人可以向我解释这个想法(最好有具体的例子),我将不胜感激。

I've been reading about LINQ to Objects, and now my colleagues want me to present it to them.

Now, I have an OK understanding of the operators and the syntax choices, but I've heard you can avoid heavy nested loops by using LINQ. I'm having trouble coming up with a good set of "before and after" code listings to demonstrate this though.

I found a great example of sorting and grouping with and without LINQ in Magennis' book, and he also has an example of writing xml. But what about those nested loops? Is this even a realistic claim, given that we usually need a foreach loop or two to iterate over the results of the query anyway?

If anyone can explain this idea to me (ideally with specific examples), I would greatly appreciate it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

预谋 2024-12-25 16:33:17

这是一种可以使用 Linq 删除的嵌套循环。

foreach(SomeClass item in Items)
{
    foreach(SomeOtherClass subItem in item.SubItems)
    {
        // ...
    }
}

它可以变成:

foreach(SomeOtherClass subItem in Items.SelectMany(i => i.SubItems))
{
}

使用 SelectMany IEnumerable上的 扩展方法。

这非常有用的一个地方是嵌套循环双断点场景

Here's a type of nested loop you can remove with Linq.

foreach(SomeClass item in Items)
{
    foreach(SomeOtherClass subItem in item.SubItems)
    {
        // ...
    }
}

It can be turned into:

foreach(SomeOtherClass subItem in Items.SelectMany(i => i.SubItems))
{
}

Using the SelectMany extension method on IEnumerable.

One place where this is quite useful is for nested loop double-break scenarios.

盗琴音 2024-12-25 16:33:17

假设您有很多产品,例如:

var products = new List<Product>
    {
        new Product { Id = 1, Category = "Electronics", Value = 15.0 },
        // etc.
    };

并且您想查找所有价值 > 的产品。 $100.0,按类别分组,您可以使用 foreach 执行此操作:

var results = new Dictionary<string, List<Product>>();

foreach (var p in products)
{
    if (p.value > 100.0)
    {
        List<Product> productsByGroup;

        if (!results.TryGetValue(p.Category, out productsByGroup))
        {
            productsByGroup = new List<Product>();
            results.Add(p.Category, productsByGroup);
        }
        productsByGroup.Add(p);
    }
}

或者,您可以简单地使用 LINQ 方法:

var results = products.Where(prod => prod.Value > 100.0)
                  .GroupBy(prod => prod.Category);

或使用 LINQ 表达式语法:

var results = from p in products 
                  where p.Value > 100.0
                  group p by p.Category;

更加简洁且不易出错。

Say you've got a lot of products, such as:

var products = new List<Product>
    {
        new Product { Id = 1, Category = "Electronics", Value = 15.0 },
        // etc.
    };

And you want to find all products with a value > $100.0, grouped by category, you could do this using foreach:

var results = new Dictionary<string, List<Product>>();

foreach (var p in products)
{
    if (p.value > 100.0)
    {
        List<Product> productsByGroup;

        if (!results.TryGetValue(p.Category, out productsByGroup))
        {
            productsByGroup = new List<Product>();
            results.Add(p.Category, productsByGroup);
        }
        productsByGroup.Add(p);
    }
}

Or, you could simply use the LINQ methods:

var results = products.Where(prod => prod.Value > 100.0)
                  .GroupBy(prod => prod.Category);

Or using LINQ expression syntax:

var results = from p in products 
                  where p.Value > 100.0
                  group p by p.Category;

Much more concise and less error-prone.

夜深人未静 2024-12-25 16:33:17
var results = new List<Object>();
foreach(var i in list)
{
    if (i.property == value)
    {
         foreach(var j in list.SubList)
         {
              if (j.other == something)
              {
                  results.push(j);
              }
         }
    }
}

可能是:

var results = list.Where(i => i == value)
                  .SelectMany(i => i.SubList)
                  .Where(j => j.other == something)
                  .ToList();
var results = new List<Object>();
foreach(var i in list)
{
    if (i.property == value)
    {
         foreach(var j in list.SubList)
         {
              if (j.other == something)
              {
                  results.push(j);
              }
         }
    }
}

could be:

var results = list.Where(i => i == value)
                  .SelectMany(i => i.SubList)
                  .Where(j => j.other == something)
                  .ToList();
花想c 2024-12-25 16:33:17

这是一个有点人为的例子。

假设给您一个字符串列表,您的任务是在 HashSet 中查找并返回这些字符串中找到的所有控制字符。

var listOStrings = new List<string> { ... };
var result = new HashSet<char>();

你可以这样做:

foreach (var str in listOStrings)
{
    foreach (var c in str)
    {
        if (Char.IsControl(c))
        {
            result.Add(c);
        }
    }
}

或者使用 LINQ:

result = new HashSet<char>(
    listOStrings
        .SelectMany(str => str.Where(Char.IsControl)));

Here's a somewhat contrived example.

Suppose you were given a list of strings and your task was to find and return all the control characters found in those strings in a HashSet<>.

var listOStrings = new List<string> { ... };
var result = new HashSet<char>();

You might do something like this:

foreach (var str in listOStrings)
{
    foreach (var c in str)
    {
        if (Char.IsControl(c))
        {
            result.Add(c);
        }
    }
}

Or using LINQ:

result = new HashSet<char>(
    listOStrings
        .SelectMany(str => str.Where(Char.IsControl)));
不喜欢何必死缠烂打 2024-12-25 16:33:17

最有用的示例是当您可以使用 LINQ 中的内置方法时,例如 AllAny。像这样:

bool hasCats = listOfAnimals.Any(animal => animal.Type == "Cat");

用带有 if 和 break 的 for 循环以及 bool 检查变量编写它,我想至少需要五行代码才能完成相同的操作。嗯,让我们看看:

bool hasCats = false;
foreach(Animal animal in listOfAnimals)
{
    if (animal.Type == "Cat")
    {
        hasCats = true;
        break;
    }
}

哎呀,9 行。并且您需要仔细阅读其中至少三篇才能了解代码的作用。

嗯,更多的是相同的。假设哺乳动物有真实的类型层次结构。

IEnumerable<Cat> allCats = listOfAnimals.OfType<Cat>();

这将返回所有可以铸造成 Cat 的动物,并返回它们,铸造并准备使用。用循环编写:

List<Cat> allCats = new List<Cat>();
foreach(var animal in listOfAnimals)
{
    var cat = animal as Cat;
    if (cat != null)
    {
        allCats.Add(cat);
    }
}

说实话,您应该将其分解为一个单独的方法,并使用 yield return cat; 来获得与 LINQ 版本相同的惰性行为。

但我更喜欢查询语法。读起来很流畅,没有什么干扰。

var cats = 
    from cat in listOfCats
    where cat.Age > 5
    where cat.Color == "White"
    select cat;

再次用普通循环编写,

List<Cat> cats = new List<Cat>();
foreach(Cat cat in listOfCats)
{
    if (cat.Age > 5)
    {
        if (cat.Color == "White")
        {
            cats.Add(cat);
        }
    }
}

需要一个带有yield return的单独方法来获得相同的惰性求值行为。

The most useful examples are when you can use the built in methods in LINQ, like All and Any. Like this:

bool hasCats = listOfAnimals.Any(animal => animal.Type == "Cat");

Write that with a for loop with if and break and a bool check variable, I guess that would be at least five lines of code to do the same. Hmm, lets see:

bool hasCats = false;
foreach(Animal animal in listOfAnimals)
{
    if (animal.Type == "Cat")
    {
        hasCats = true;
        break;
    }
}

ooops, 9 lines. And you need to read at least three carefully of them to know what the code does.

Well, more of the same. Assuming the mammals have a real type hierarchy.

IEnumerable<Cat> allCats = listOfAnimals.OfType<Cat>();

This returns all animals that can be casted into a Cat and returns them, casted and ready to use. Written with loops:

List<Cat> allCats = new List<Cat>();
foreach(var animal in listOfAnimals)
{
    var cat = animal as Cat;
    if (cat != null)
    {
        allCats.Add(cat);
    }
}

To be honest you should break that out to a separate method and use yield return cat; to get the same lazy behaviour as the LINQ version.

But I prefer the query syntax. It is nice and fluent to read with very little noice.

var cats = 
    from cat in listOfCats
    where cat.Age > 5
    where cat.Color == "White"
    select cat;

Written with plain loops

List<Cat> cats = new List<Cat>();
foreach(Cat cat in listOfCats)
{
    if (cat.Age > 5)
    {
        if (cat.Color == "White")
        {
            cats.Add(cat);
        }
    }
}

again a separate method with yield return would be required to get the same lazy evaluation behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文