2011-12-28 32 views
1

我有,類似於一個片段一個XDocument:如何從平面XDocument加入組?

<Data> 
    <Row Id="0" ParentId="-1"> 
     <!-- stuff --> 
    </Row> 
    <Row Id="1" ParentId="0"> 
     <!-- stuff --> 
    </Row> 
    <Row Id="2" ParentId="0"> 
     <!-- stuff --> 
    </Row> 
    <Row Id="3" ParentId="-1"> 
     <!-- stuff --> 
    </Row> 
    <Row Id="4" ParentId="3"> 
     <!-- stuff --> 
    </Row> 
</Data> 

假設嵌套僅限於上面的例子。 我想創建一個數據結構 - IDictionary<Parent, List<Child>>。我似乎無法得到任何正確連接。我有這一點是:

// get lists of data nodes 
List<XElement> pRows = xData.Elements(XName.Get("Row")) 
          .Where(e => e.Attribute(XParentId).Value == "-1") 
          .Select(e => e) 
          .ToList(); 
List<XElement> cRows = xData.Elements(XName.Get("Row")) 
          .Where(e => e.Attribute(XParentId).Value != "-1") 
          .Select(e => e) 
          .ToList(); 

var dataSets = pRows.GroupJoin(cRows, 
           p => p, 
           c => c.Attribute(XParentId).Value, 
           (p, children) => new { 
           ParentID = p.Attribute(XId).Value, 
           Children = children.Select(c => c) 
           }); 

編譯器抱怨:

類型參數的方法 「System.Linq.Enumerable.GroupJoin(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable, System.Func,System.Func, System.Func,TResult>)' 無法根據用法推斷。嘗試明確指定類型參數 。

我跟着MSDN using the GroupJoin的樣本。我不想使用2個列表 - 我寧願使用包含所有行的單個列表List<XElement>

回答

1

我認爲2列表的方法更清潔,除非我最好避免直接調用ToList(),或者確實需要列表。你可以把它變成一個聲明,但它會很難跟隨。

要解決您的查詢,您需要將pRows的外鍵選擇器從p => p更改爲p => p.Attribute(XId).Value,這是實際的ID。目前它選擇整個元素,由於它們是不同類型,因此無法與c => c.Attribute(XParentId).Value進行比較。更新後的查詢是:

var dataSets = pRows.GroupJoin(cRows, 
           p => p.Attribute(XId).Value, 
           c => c.Attribute(XParentId).Value, 
           (p, children) => new { 
           ParentID = p.Attribute(XId).Value, 
           Children = children.Select(c => c) 
           }); 

要避免使用2只列出了您可以修改上面的查詢和替換pRowscRows各自的查詢,但是這使得它漫長而困難的眼睛。在這種特殊情況下,我更喜歡錶達GroupJoin使用查詢語法,因爲它是更容易比流利的語法如下:

var query = from root in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value == "-1") 
      join child in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value != "-1") 
      on root.Attribute("Id").Value equals child.Attribute("ParentId").Value 
      into rootChild 
      select new 
      { 
       ParentId = root.Attribute("Id").Value, 
       Children = rootChild.Select(o => o) 
      }; 

var dict = query.ToDictionary(o => o.ParentId, o => o.Children.ToList()); 

如果你真正的問題有更多的嵌套,LINQ可能不會有理想的解決方案。

0

首先,爲了讓您的代碼能夠編譯,您可能需要將GroupJoin中的參數p => p替換爲p => p.Attribute(XId).Value,它會選擇用於比較的關鍵字。

其結果,你會得到一個IEnumerable與對象

  • PARENTID = 0,IEnumerable<XElement> {行ID = 1時,行ID = 2}
  • PARENTID = 3,IEnumerable<XElement> {行ID = 4}

當然,你也可以改變.Select(c => c)返回與剛剛的ID(.Select(c => c.Attribute(XId).Value).ToList())一List<string>,但你仍然失蹤PARENTID = -1,而你沒有有一本字典。

如果要包括PARENTID = -1,也得到了Dictionary<string,List<string>>,那麼你可能想嘗試這(爲一個點,從開始),它採用了不同的方法:

// get all ParentIds 
var allParentIds = xData.Elements(XName.Get("Row")) 
         .Select(e => e.Attribute(XParentId).Value) 
         .Distinct(); 
// then use them to get all Ids where the ParentId is in the "list" of all ParentIds 
var parentIdsAndChilds = from givenIds 
         in allParentIds 
         select new { 
          Id = givenIds, 
          Childs = xData.Elements(XName.Get("Row")).Where(e => e.Attribute(XParentId).Value == givenIds).Select(e => e.Attribute(XId).Value).ToList() 
         }; 
// if needed, convert it to a Dictionary 
Dictionary<string, List<string>> dict = parentIdsAndChilds.ToDictionary(k => k.Id, v => v.Childs); 

希望這有助於提供進一步研究的起點。

一個側面說明:正如你可能知道,LINQ使用延遲執行所以你可能不希望調用ToList()每一步,以節省執行時間(具體取決於數據的總量,這可能是一個好主意無論如何現在使用ToList(),因爲它可能會在處理過程中節省內存,但這取決於實際數據)。