Linq将一对多关系简化为一对一

时间:2015-11-19 23:21:55

标签: c# linq linq-to-objects

List<dynamic> a = new List<dynamic>();
a.Add(new { Foo = 1, Baz = "Inga", Name = "Alice"});
a.Add(new { Foo = 2, Baz = "Baz", Name = "Bob"});
a.Add(new { Foo = 3, Baz = "Hi", Name = "Charlie"});

List<dynamic> b = new List<dynamic>();
b.Add(new { Foo = 1, Value = "Bar", Code = "A"});
b.Add(new { Foo = 1, Value = "Quux", Code = "B"});
b.Add(new { Foo = 2, Value = "Bar", Code = "C"});
b.Add(new { Foo = 3, Value = "Mint", Code = "A"});
b.Add(new { Foo = 3, Value = "Seven", Code = "Q"});
b.Add(new { Foo = 3, Value = "Threeve", Code = "T"});

好的....所以我有一个问题(自然) 这是设计和简化的,专注于手头的问题。 我需要修改Linq查询以将两个列表投影到以下响应:

[
   {  Foo = 1
    , Baz = "Inga"
    , Code = "A"
    , Bars = [{ Value = "Bar", Code = "A"}
             ,{ Value = "Quux", Code = "B"}
             ]
   }
   ,{  Foo = 2
     , Baz = "Baz"
     , Code = "C"
     , Bars = [{ Value = "Fizz", Code = "C"}]
    }
   ,{  Foo = 3
     , Baz = "Hi"
     , Code = "A"
     , Bars = [{ Value = "Mint", Code = "A"}
              ,{ Value = "Seven", Code = "Q"}
              ,{ Value = "Threeve", Code = "T"}
              ]
    }
]

首先,TL; DR

  

有没有办法查询集合b来选择

     

(b.First Where Distinct By b.Foo) AsEnumerable()

...长版本

我需要选择a的投影,但在实现时,确定列表Code中的第一个b b.Foo == a.Foo并放置b.Code直接在a。然后是b中的b.Foo == a.Foo需要放入a.Bars的项目。

我手上的问题是我没有识别单a因此我无法预先选择ab值来简化这个混乱,并且没有机会重塑。

所以,如果我想搜索Where Value = Bar;需要使用正确的映射和投影返回Alice和Bob。

天真的尝试将是......

var results = a.Join( b
              , master => master.Foo
              , detail => detail.Foo
              , (master, detail) => new { master, detail})
         .Select(item => new
         {
             item.master.Foo
           , item.master.Baz
           , item.master.Name
           , item.detail.Code
           , Bars = b.Select(x => x.Foo.Equals(item.master.Foo))
         };

但是这导致我的结果包含重复的“Alice”记录和重复的“Charlie”记录,因为它内部加入了a和b。我真正想做的事(伪)是

a.Join(
        b.Where(b.Foo.Equals(a.Foo)).First()
        , master => master.Foo
        , detail => detail.Foo
        , (master, detail) => new { master, detail}
      )
 .Select(item => new
         {
             item.master.Foo
           , item.master.Baz
           , item.master.Name
           , item.detail.Code
           , Bars = b.Select(x => x.Foo.Equals(item.master.Foo))
         };

但无论我尝试什么,它都会变得一团糟。

  

...注意,我不能采用天真的方法,然后运行DistinctBy,因为投影是匿名的。

任何人都可以使用Linq to Object查询解决这个问题吗? (注意:我不需要单次通过决议)

4 个答案:

答案 0 :(得分:2)

我会坚持将第二组(在此处表示为b)分组,然后使用与find配对的组合投影。

var results = b.GroupBy( d => d.Foo ).Select( g => new {
    Foo = g.Key,
    Baz = a.First( i => i.Foo == g.Key ).Baz,
    Code = g.First().Code,
    Bars = g.Select( e => new { Value = e.Value, Code = e.Code }).ToArray()
});

enter image description here

答案 1 :(得分:1)

如果我理解正确,单个结果条目中的Code只是第一个加入的Code元素的b

所以试试这个:

var result = a.GroupJoin(b,
                         a0 => a0.Foo,
                         b0 => b0.Foo,
                         (a0, bs) =>
                             new
                             {
                                 Foo = a0.Foo,
                                 Baz = a0.Baz,
                                 Code = bs.Select(b1 => b1.Code).FirstOrDefault(),
                                 Bars = bs.Select(b1 => new {b1.Value, b1.Code}).ToArray()
                             }).ToArray();

答案 2 :(得分:0)

GroupJoin就是你需要的。您可以将Join视为SelectManyGroupJoin - 与Select一样。不同之处在于投影的第二个参数的类型 - TInner的{​​{1}}和Join的{​​{1}}。在LINQ语法中,IEnumerable<TInner>GroupJoin子句实现。

尽管如此,以下是两种语法中的示例:

GroupJoin

答案 3 :(得分:0)

var query = from ai in a
            let bs = b.Where(bi => bi.Foo == ai.Foo)
            select new
            {
                ai.Foo,
                ai.Baz,
                Code = bs.Select(bi => bi.Code).FirstOrDefault(),
                Bars = bs.Select(bi => new { bi.Value, bi.Code }),
            };