使用Linq查询丢失foreach

时间:2014-03-14 19:28:20

标签: c# linq foreach

我想知道是否可以用某种方式用LINQ查询替换这个foreach :(如果可能的话):

在线围栏: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; }
}

3 个答案:

答案 0 :(得分: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();

答案 1 :(得分:2)

为了扩展@Servy的例子,我经常发现LINQ表达式语法比lambda SelectMany更容易阅读(它转换为相同的东西)。这是他使用查询表达式的查询:

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

答案 2 :(得分: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从新{{1}} s中制作单个平面列表。