2014-03-14 4 views
1

мне было интересно, если я мог бы заменить этот foreach с запросами LINQ как-то (если это возможно):Потеря Еогеасп с Linq запросов

Интернет манежа: https://ideone.com/PQEytf

using System; 
using System.Collections.Generic; 
using System.Linq; 

public class Test 
{ 
    public static void Main() 
    { 
     // dummy inputs for test sake 
     var keys = new[] { "key1", "key2" }; 

     var services = new Dictionary<string, Service> 
     { 
      {"key1", new Service {Components = new Dictionary<string, string> {{"comp1", "value1"}}}}, 
      {"key2", new Service {Components = new Dictionary<string, string> {{"comp2", "value2"}}}} 
     }; 


     var serviceComponents = GetServiceComponents(keys, services); 
     // do something with it 
    } 

    public static IEnumerable<ServiceComponent> GetServiceComponents(string[] keys, Dictionary<string, Service> services) 
    { 
     var serviceComponents = new List<ServiceComponent>(); 

     // this is the foreach that I want to lose 
     foreach (
      var key in 
       keys.Select(
        name => 
         services.FirstOrDefault(
          x => x.Key.Equals(name)))) 
     { 
      serviceComponents.AddRange(key.Value.Components.Select(component => new ServiceComponent 
      { 
       Name = key.Key, 
       Service = component.Key, 
       Value = component.Value, 
      })); 
     } 

     return serviceComponents.ToArray(); 

    } 
} 

public class Service 
{ 
    public Dictionary<string, string> Components { get; set; } 
} 

public class ServiceComponent 
{ 
    public string Name { get; set; } 
    public string Service { get; set; } 
    public string Value { get; set; } 
} 

ответ

1

Что вам нужно SelectMany, однако, с помощью FirstOrDefault вы открываете себя до NullReferenceException (как значение по умолчанию для любого ссылочного типа является null). Если вы намерены создать NullReferenceException, когда элемент keys не является ключом в services, тогда вы можете использовать другие ответы, которые используют SelectMany. Тем не менее, это вы не собираетесь NullReferenceException, то вы должны использовать что-то вроде следующего:

return services.Where(pair => keys.Contains(pair.Key)) 
       .SelectMany(pair => 
          pair.Value.Components 
           .Select(component => new ServiceComponent 
                 { 
                  Name = pair.Key, 
                  Service = component.Key, 
                  Value = component.Value 
                  })) 
       .ToArray(); 

Это утверждение удаляет все пары ключ-значение из services, ключ не в keys массиве, затем поворачивает каждый элемент каждая пара Components в новый объект ServiceComponent, с SelectMany, делая один плоский список из нового ServiceComponent.

+0

Мне нравится отказоустойчивость этого решения! – Mrchief

7

Да, что вы ищете, SelectMany. Это позволяет вам повернуть каждый элемент в последовательности в другую последовательность, а затем сгладить все эти последовательности в одну последовательность. (Вы выполнение то же самое, без отложенного исполнения, поставив все последовательности в список.)

return keys.SelectMany(name => services.FirstOrDefault(x => x.Key.Equals(name)) 
    .Value.Components 
    .Select(component => new ServiceComponent 
    { 
     Name = name.Key, 
     Service = component.Key, 
     Value = component.Value, 
    })) 
    .ToArray(); 

Сказав, что это запрос делает, принимая каждый из ваших ключей, находя соответствующий элемент в услугах с использованием линейного поиска, а затем сопоставление результата. Вместо того, чтобы делать линейный поиск, используя FirstOrDefault, вы можете использовать нативную способность словаря эффективны и эффективно находить значения для каждого ключа:

return keys.SelectMany(key => services[key].Components 
    .Select(component => new ServiceComponent 
    { 
     Name = key, 
     Service = component.Key, 
     Value = component.Value, 
    })) 
    .ToArray(); 
+0

Я знал, что ответ будет вращаться вокруг 'SelectMany', но не мог на него положиться. Кроме того, ваш второй пункт о потере 'FirstOrDefault' - обледенение на торте! Благодаря! – Mrchief

2

Продлить @ Например Servy, я часто синтаксис выражений LINQ будет легче прочитайте, чем лямбда SelectMany (это означает то же самое). Вот его запрос с помощью выражений запросов:

return from key in keys 
     from component in services[key].Components 
     select new ServiceComponent 
     { 
      Name = key, 
      Service = component.Key, 
      Value = component.Value, 
     })) 
     .ToArray(); 
+0

Это не называется синтаксисом LINQ, это * синтаксис запроса *. LINQ - это имя, присвоенное методам в пространстве имен System.Linq. Оба решения используют LINQ. – Servy

+0

Это синтаксис языкового интегрированного запроса (LINQ). Это выражение с выражением запроса, а не лямбда-выражениями. –

+0

[MSDN определяет это как синтаксис запроса] (http://msdn.microsoft.com/en-us/library/bb397947.aspx), а не синтаксис LINQ. Синтаксис LINQ - это не вещь, по крайней мере, официально. Другой синтаксис - это синтаксис метода, а не лямбда-синтаксис. Оба из них представляют собой два разных синтаксиса для написания запроса LINQ. – Servy

Смежные вопросы