Как использовать LINQ для исключения вложенных циклов?
Я читал о LINQ to Objects, и теперь мои коллеги хотят, чтобы я представил их им.
Теперь у меня есть хорошее понимание операторов и вариантов синтаксиса, но я слышал, что вы можете избегать тяжелых вложенных циклов с помощью LINQ. У меня возникли проблемы с хорошим набором списков кодов "до и после", чтобы продемонстрировать это.
Я нашел отличный пример сортировки и группировки с LINQ и без него в книге Magennis, и у него также есть пример написания xml. Но как насчет этих вложенных петель? Является ли это даже реалистичным утверждением, учитывая, что нам обычно нужен цикл foreach
или два, чтобы итерации по результатам запроса?
Если кто-нибудь может объяснить эту идею мне (в идеале с конкретными примерами), я был бы очень признателен.
Ответы
Ответ 1
Скажите, что у вас много продуктов, таких как:
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;
Гораздо более кратким и менее подверженным ошибкам.
Ответ 2
Здесь тип вложенного цикла, который вы можете удалить с помощью Linq.
foreach(SomeClass item in Items)
{
foreach(SomeOtherClass subItem in item.SubItems)
{
// ...
}
}
Его можно преобразовать в:
foreach(SomeOtherClass subItem in Items.SelectMany(i => i.SubItems))
{
}
Используя метод расширения SelectMany
на IEnumerable
.
Одно место, где это весьма полезно, - это сценарии двойного разрыва вложенных циклов.
Ответ 3
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();
Ответ 4
Вот несколько надуманный пример.
Предположим, вам предоставлен список строк, и ваша задача состояла в том, чтобы найти и вернуть все управляющие символы, найденные в этих строках, в 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)));
Ответ 5
Наиболее полезные примеры: когда вы можете использовать встроенные методы в LINQ, например All
и Any
. Вот так:
bool hasCats = listOfAnimals.Any(animal => animal.Type == "Cat");
Запишите это с циклом for с переменными if и break и bool check, я думаю, что это будет по крайней мере пять строк кода, чтобы сделать то же самое. Хмм, давайте посмотрим:
bool hasCats = false;
foreach(Animal animal in listOfAnimals)
{
if (animal.Type == "Cat")
{
hasCats = true;
break;
}
}
ooops, 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
, чтобы получить такое же ленивое поведение оценки.