Scala flatMap.toMap 类似 C# 中的 Scala flatMap.toMap 类似物

本文关键字:Scala toMap flatMap 类似物 中的 类似 | 更新日期: 2023-09-27 18:30:31

前几天我用 C# 编写了一些代码来获取一个对象列表,每个对象都包含一个数组字段,并将其转换为映射(即字典),其中对象成为值,对象数组字段的每个元素都成为键。这可能没有多大意义,所以让我们展示一些代码。在 Scala 中,我可能会这样做:

// Data class
class MyClass(val keys:Array[String])
object MyClassTests {
  // Dummy method just to get some sample data
  def getMyClassList() = List(
    new MyClass(Array("one", "two", "three")),
    new MyClass(Array("four", "five", "six")))

  def test() = {
    val list = getMyClassList()
    val map1 = list.view.flatMap(mc => mc.keys.map(_ -> mc)).toMap
    // or, if you like for-comprehensions:
    val map2 = (for (mc <- list.view; k <- mc.keys) yield k -> mc).toMap
    map1 // or map2, same difference
  }
}

在 REPL 中运行它,给我们(添加了格式):

res0: scala.collection.immutable.Map[String,MyClass] = 
    Map(four -> MyClass@4ee31ef2, 
        three -> MyClass@69bc6271, 
        two -> MyClass@69bc6271, 
        six -> MyClass@4ee31ef2, 
        five -> MyClass@4ee31ef2, 
        one -> MyClass@69bc6271)

但是,我不想在 Scala 中这样做,我想在 C# 中这样做。下面给出了两种可能的解决方案,Test1()是一个非常命令式的解决方案,Test2()是我当场能想到的尽可能接近的类似物。

所以我的问题是:忽略这个例子看似无用的事实,Test2() 真的是最接近 Scala 代码的类似物吗,它是在 C# 中执行此操作的最简洁的方法吗?有没有更简洁的方法来完成相同的任务(在 C# 中)?

// Data class
class MyClass {
    public string[] Keys { get; set; }
}
class MyClassTests {
    // Dummy method just to get some sample data
    public static IList<MyClass> GetMyClassList() {
        return new List<MyClass> {
            new MyClass {
                Keys = new[] {"one", "two", "three"}
            },
            new MyClass {
                Keys = new[] {"four", "five", "six"}
            }
        };
    }
    public void Test1() {
        var list = GetMyClassList();
        var validTypes = new Dictionary<string, MyClass>();
        foreach (var h in list) {
            foreach (var key in h.Keys)
                validTypes.Add(key, h);
        }
    }
    public void Test2() {
        var list = GetMyClassList();
        var validPartTypes = list
            .SelectMany(mc => mc.Keys.Select(k => new KeyValuePair<string,MyClass>(k, mc)))
            .ToDictionary(x => x.Key, x => x.Value);
    }
}

Scala flatMap.toMap 类似 C# 中的 Scala flatMap.toMap 类似物

您可以使用

匿名类型而不是KeyValuePair<>来使其更简洁:

var validPartTypes = list.SelectMany(mc => mc.Keys.Select(k => new { k, mc }))
                         .ToDictionary(x => x.k, x => x.mc);

或者,如果您觉得 LINQ 查询语法更清晰,则为:

var validPartTypes = (from mc in list
                      from k in mc.Keys
                      select new { k, mc })
                      .ToDictionary(x => x.k, x => x.mc);

但是,您的代码是完全有效的,我没有看到任何更好的方法可以在 C# 中实现这一目标。

您可以创建自己的类似 LINQ 的方法:

public static class MyLinq
{
    public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> t)
    {
        return t.ToDictionary(p => p.Key, p => p.Value);
    }
}

你也可以使用 ToDictionary 代替 .Select(k => new KeyValuePair ,但由于创建了许多无用的字典,这是一种非常缓慢的方法:

public void Test2() { 
    var list = GetMyClassList(); 
    var validPartTypes = list 
        .SelectMany(mc => mc.Keys.ToDictionary(k => mc)) 
        .ToDictionary(); 
}