Как использовать LINQ, чтобы избежать вложенных циклов?
Я читал о LINQ для объектов, и теперь мои коллеги хотят, чтобы я представил их им.
теперь у меня есть хорошее понимание операторов и синтаксических вариантов, но я слышал, что вы можете избегайте тяжелых вложенных циклов С помощью LINQ. У меня возникли проблемы с созданием хорошего набора списков кода "до и после", чтобы продемонстрировать это.
Я нашел отличный пример сортировки и группировки с LINQ и без него в книге Magennis, и он также есть пример написания xml. Но как насчет вложенных циклов? Это даже реалистичное утверждение, учитывая, что нам обычно нужно foreach
цикл или два для итерации результатов запроса в любом случае?
Если кто-нибудь может объяснить мне эту идею (в идеале с конкретными примерами), я был бы очень признателен.
5 ответов
скажем, у вас есть много продуктов, таких как:
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;
гораздо более краткой и менее подверженным ошибкам.
вот тип вложенного цикла, который вы можете удалить с помощью Linq.
foreach(SomeClass item in Items)
{
foreach(SomeOtherClass subItem in item.SubItems)
{
// ...
}
}
его можно превратить в:
foreach(SomeOtherClass subItem in Items.SelectMany(i => i.SubItems))
{
}
используя на SelectMany
метод расширения на IEnumerable
.
одно место, где это очень полезно для вложенные циклические сценарии двойного разрыва.
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();
вот несколько надуманный пример.
Предположим, вам дали список строк, и ваша задача заключалась в том, чтобы найти и вернуть все управляющие символы, найденные в этих строках в 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)));
наиболее полезными примерами являются случаи, когда вы можете использовать встроенные методы в LINQ, например All
и Any
. Вот так:
bool hasCats = listOfAnimals.Any(animal => animal.Type == "Cat");
напишите это с циклом for с if и break и переменной проверки bool, я думаю, что это будет по крайней мере пять строк кода, чтобы сделать то же самое. Хм, давайте посмотрим:
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;
написано с помощью plain петли
List<Cat> cats = new List<Cat>();
foreach(Cat cat in listOfCats)
{
if (cat.Age > 5)
{
if (cat.Color == "White")
{
cats.Add(cat);
}
}
}
снова отдельный метод с yield return
потребуется, чтобы получить такое же ленивое поведение оценки.