Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala flatMap.toMap analogues in C#

Tags:

c#

scala

I was writing some code in C# the other day to take a list of objects, each containing an array field, and convert it to a map (i.e. dictionary) where the objects become the values, and each element of the object's array field become the keys. That might not have made much sense, so let's show some code. In Scala, I might do it something like this:

// 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
  }
}

Running that in the REPL, gives us (with formatting added):

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)

However, I didn't want to do this in Scala, I wanted to do it in C#. The below presents two possible solutions to this, Test1() being a very imperative-style solution, and Test2() being as close an analogue as I could think up on the spot.

So my question is this: Ignoring the fact that the example is seemingly inane, is Test2() really the closest analogue to the Scala code, and is it the most concise way to do this in C#? Is there a more concise way to accomplish the same task (in 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);
    }
}
like image 946
DK_ Avatar asked Feb 21 '23 21:02

DK_


1 Answers

You can use an anonymous type instead of the KeyValuePair<> to make it a bit conciser:

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

or, if you feel LINQ query syntax clearer, it would be:

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

However your code is perfectly valid and I don't see any much better way to accomplish that in C#.

like image 68
digEmAll Avatar answered Feb 23 '23 09:02

digEmAll