【问题标题】:Convert Lambda into Linq Statement with nested for loop使用嵌套 for 循环将 Lambda 转换为 Linq 语句
【发布时间】:2012-12-29 20:56:35
【问题描述】:

有没有办法重写下面的GetTransformedCollection 方法,使其使用Linq 语句而不是表达式?我目前正在尝试解决“无法将带有语句体的 lambda 表达式转换为表达式树”错误。

public class Obj1
{
    public int Id { get; set; }
    public string[] Names { get; set; }
    public string[] Tags { get; set; }
}

public class EntCollections
{
    private List<Obj1> _results;

    [SetUp]
    public void SetUp()
    {
        _results = new List<Obj1>
        {
            new Obj1 {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
            new Obj1 {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
            new Obj1 {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}}
        };
    }

    private static Dictionary<string, List<string>>
        GetTransformedCollection(IEnumerable<Obj1> results)
    {
        var list = new Dictionary<string, List<string>>();

        foreach (var result in results)
        {
            foreach (var id in result.Names)
            {
                if (list.ContainsKey(id))
                {
                    list[id].AddRange(result.Tags);
                }
                else
                {
                    list.Add(id, result.Tags.ToList());
                }
            }
        }

        return list;
    }

    [Test]
    public void Test()
    {
        var list = GetTransformedCollection(_results);

        Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
        Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
        Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
    }

P.s 我不太担心结果类型是字典,这只是将其表示为返回类型的最简单的方式。

【问题讨论】:

  • 你的意思是用 Linq 替换 foreach?
  • 尝试使用 Linq All() 或 ForEach() 扩展。

标签: c# linq lambda


【解决方案1】:

我个人会使用ILookup,只要你有Dictionary&lt;T1, List&lt;T2&gt;&gt;,这是一个不错的选择,并且是用ToLookup()构建的:

// Flatten the objects (lazily) to create a sequence of valid name/tag pairs
var pairs = from result in results
            from name in result.Names
            from tag in result.Tags
            select new { name, tag };

// Build a lookup from name to all tags with that name
var lookup = pairs.ToLookup(pair => pair.name, pair => pair.tag);

【讨论】:

  • +1。比使用扩展方法语法更简洁的解决方案
  • 我正在采用这种方法,因为它只枚举列表一次。有没有办法在列表上执行“外部连接”?我的意思是,如果原始列表中存在一个元素,其名称为 n,但标签为 0,那么各个名称仍将作为一对 null/ 出现在结果集中空字符串?我在原始列表中添加了这样一个元素并添加了一个额外的断言。
  • 这听起来像是你想要GroupJoin,基本上。这可能是一个截然不同的答案 - 我建议您将其作为一个单独的问题提出,因为它会有多么不同。
  • 还删除了对这个问题的修改,所以答案仍然有效。
【解决方案2】:

想法是找到结果字典的所有键,然后从Obj1的原始序列中找到对应的值

var distinctNames = results.SelectMany(val => val.Names).Distinct();

return distinctNames
      .ToDictionary(name => name, 
                    name => results
                            .Where(res => res.Names.Contains(name))
                            .SelectMany(res => res.Tags)
                            .ToList());

【讨论】:

  • 说实话,我发现这个机制更容易想象,谢谢。
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2017-02-19
  • 1970-01-01
  • 2013-03-05
  • 1970-01-01
  • 2012-11-06
  • 2011-06-07
  • 1970-01-01
相关资源
最近更新 更多