2011-03-04 197 views
5

今天我遇到了一個問題,並且我一直努力嘗試獲得我正在尋找的結果。嵌套的LINQ查詢問題

目前我有類似以下的類:

public class InstanceInformation 
{ 
    public string PatientID {get; set;} 
    public string StudyID {get; set;} 
    public string SeriesID {get; set;} 
    public string InstanceID {get; set;} 
} 

我有一個List<InstanceInformation>,我試圖使用LINQ(或任何其他方式爲基於文件的目錄)生成路徑(

PatientID/StudyID/SeriesID/InstanceID 

我的問題是,目前的數據是非結構化的,因爲它涉及在前面提到的形式(列表),我需要一種方法來組中的所有數據與FOLL:此列表類似於下面的上由於限制:

  • 集團InstanceIDs通過SeriesID
  • 集團SeriesIDs通過StudyID
  • 集團StudyIDs通過PatientID

我現在有一些類似於此:

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from studyGroups in 
       (from instance in patientGroups 
        group instance by instance.StudyID) 
        from seriesGroup in 
         (from instance in studyGroups 
         group instance by instance.SeriesID) 
          from instanceGroup in 
           (from instance in seriesGroup 
            group instance by instance.InstanceID) 
      group instanceGroup by patientGroups.Key; 

它只是通過PatientID對我所有的InstanceID進行分組,並且很難在此大量分組後查看所有數據以查看(StudyID/SeriesID)之間的區域是否丟失。任何解決這個問題的其他方法都會受到歡迎。

這主要是隻爲分組的對象 - 因爲我需要再遍歷它們(使用foreach)

+0

這會有幫助嗎? var result = instances.GroupBy(i => new {i.InstanceID,i.SeriesID,i.StudyID,i.PatientID}) – WorldIsRound 2011-03-04 21:48:16

+0

Nope - 感謝HelloWorld,這只是列表中的所有項目列表。我認爲我遇到的問題是嘗試使用LINQ來建立關係。 – 2011-03-04 22:01:20

回答

11

我不知道你提出的查詢是你真正想要還是需要的查詢,但假設它是這樣的,我們來考慮是否重新編寫它是一種更好的方式。

你想要看的地方是C#4規範的第7.16.2.1,其中一部分我在這裏引用爲了您的方便:


與延續查詢表達式

from ... into x ... 

被翻譯成

from x in (from ...) ... 

清楚了嗎?讓我們來看看您的查詢的片段,我已經標有星:

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from studyGroups in 
       **** (from instance in patientGroups 
        group instance by instance.StudyID) **** 
        from seriesGroup in 
         (from instance in studyGroups 
         group instance by instance.SeriesID) 
          from instanceGroup in 
           (from instance in seriesGroup 
            group instance by instance.InstanceID) 
      group instanceGroup by patientGroups.Key; 

在這裏,我們有

from studyGroups in (from ...) ... 

規範說,這相當於

from ... into studyGroups ... 

所以我們可以重寫您的查詢爲

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from instance in patientGroups 
      group instance by instance.StudyID into studyGroups 
      from seriesGroup in 
      **** (from instance in studyGroups 
        group instance by instance.SeriesID) **** 
         from instanceGroup in 
          (from instance in seriesGroup 
          group instance by instance.InstanceID) 
      group instanceGroup by patientGroups.Key; 

再來一次。現在我們有

from seriesGroup in (from ...) ... 

和規範說,這是一樣的

from ... into seriesGroup ... 

因此改寫這樣的:

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from instance in patientGroups 
      group instance by instance.StudyID into studyGroups 
      from instance in studyGroups 
      group instance by instance.SeriesID into seriesGroup 
      from instanceGroup in 
       ****  (from instance in seriesGroup 
        group instance by instance.InstanceID) **** 
      group instanceGroup by patientGroups.Key; 

又一次!

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from instance in patientGroups 
      group instance by instance.StudyID into studyGroups 
      from instance in studyGroups 
      group instance by instance.SeriesID into seriesGroup 
      from instance in seriesGroup 
      group instance by instance.InstanceID into instanceGroup 
      group instanceGroup by patientGroups.Key; 

我希望你的同意是一個更容易閱讀。我會通過更改「實例」用於半打次,意味着不同的事情的事實提高其可讀性更多:

var groups = from instance in instances 
      group instance by instance.PatientID into patientGroups 
      from patientGroup in patientGroups 
      group patientGroup by instance.StudyID into studyGroups 
      from studyGroup in studyGroups 
      group studyGroup by studyGroup.SeriesID into seriesGroups 
      from seriesGroup in seriesGroups 
      group seriesGroup by seriesGroup.InstanceID into instanceGroup 
      group instanceGroup by patientGroups.Key; 

無論這其實是你需要解決你的問題查詢,我不知道,但至少這一個你可以推理,而不是試圖關注所有的嵌套。

這種技術被稱爲「查詢延續」。基本上這個想法是延續到目前爲止在查詢中引入了一個新的範圍變量。

+1

你知道,我並不總是很理解你的答案,但我總是從他們身上學到很多東西。最後一個例子中'groups'的數據類型是什麼? – 2011-03-04 23:21:10

+1

感謝您的回答,現在已收到此問題。之前有人開玩笑說,但你真的應該將這些答案發布到一本書中:)(不是開玩笑) – 2011-03-04 23:40:31

+0

我正確的說,你最後的查詢實際上只是一系列'GroupBy'調用(可以直接使用方法調用編寫) ? – 2011-03-05 00:49:09

2

我認爲這會產生你在找什麼:

public class InstanceInformation { 
    public string PatientID { get; set; } 
    public string StudyID { get; set; } 
    public string SeriesID { get; set; } 
    public string InstanceID { get; set; } 

    public override string ToString() { 
     return String.Format("Series = {0} Study = {1} Patient = {2}", SeriesID, StudyID, PatientID); 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     List<InstanceInformation> infos = new List<InstanceInformation>() { 
      new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P1" }, 
      new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P1" }, 
      new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P2" }, 
      new InstanceInformation(){ SeriesID = "A", StudyID = "A2", PatientID = "P1" }, 
      new InstanceInformation(){ SeriesID = "B", StudyID = "B1", PatientID = "P1"}, 
      new InstanceInformation(){ SeriesID = "B", StudyID = "B1", PatientID = "P1"}, 
     }; 

     IEnumerable<IGrouping<string, InstanceInformation>> bySeries = infos.GroupBy(g => g.SeriesID); 
     IEnumerable<IGrouping<string, InstanceInformation>> byStudy = bySeries.SelectMany(g => g.GroupBy(g_inner => g_inner.StudyID)); 
     IEnumerable<IGrouping<string, InstanceInformation>> byPatient = byStudy.SelectMany(g => g.GroupBy(g_inner => g_inner.PatientID)); 

     foreach (IGrouping<string, InstanceInformation> group in byPatient) { 
      Console.WriteLine(group.Key); 
      foreach(InstanceInformation II in group) 
       Console.WriteLine(" " + II.ToString()); 
     } 
} 
+0

這不是一個真正的groupby(因爲你分組,然後reflatten所有),但更類似於OrderBy()。ThenBy()。ThenBy()...' – digEmAll 2011-03-04 22:25:03

+0

你是對的。儘管如此,它確實會讓他尋找的羣體。根據他想要檢索數據的方式,他可能希望您的解決方案 – 2011-03-04 22:29:57

2

在你的類中覆蓋了tostring方法;如下所示。

public class InstanceInformation 
    { 
     public string PatientID { get; set; } public string StudyID { get; set; } public string SeriesID { get; set; } public string InstanceID { get; set; } 
     public override string ToString() 
     { 
      var r = string.Format("{0}/{1}/{2}/{3}", PatientID, StudyID, SeriesID, InstanceID); 
      return r; 
     } 
    } 

var listofstring = list.ConvertAll<string>(x => x.ToString()).ToList(); 
var listofstringdistinct = listofstring.Distinct().ToList(); 

這樣比較容易閱讀和理解。

+0

修復了您的代碼格式化問題:) – 2011-03-04 22:17:42

2

不exacly知道你需要什麼,但是這個(很長的代碼)將返回一個字典(詞典...)如你所說分組(即PatientID/StudyID/SeriesID/InstanceID):

var byPatient = new Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<string, InstanceInformation>>>>(); 
foreach (var patientGroup in instances.GroupBy(x => x.PatientID)) 
{ 
    var byStudy = new Dictionary<string, Dictionary<string, Dictionary<string, InstanceInformation>>>(); 
    byPatient.Add(patientGroup.Key, byStudy); 
    foreach (var studyGroup in patientGroup.GroupBy(x => x.StudyID)) 
    { 
     var bySeries = new Dictionary<string, Dictionary<string, InstanceInformation>>(); 
     byStudy.Add(studyGroup.Key, bySeries); 
     foreach (var seriesIdGroup in studyGroup.GroupBy(x => x.SeriesID)) 
     { 
      var byInstance = new Dictionary<string, InstanceInformation>(); 
      bySeries.Add(seriesIdGroup.Key, byInstance); 
      foreach (var inst in seriesIdGroup) 
      { 
       byInstance.Add(inst.InstanceID, inst); 
      } 
     } 
    } 
} 

附:
我認爲InstanceID在所有實例中都是唯一的。

否則,最後詞典水平應該是:Dictionary<string, List<InstanceInformation>>

編輯:

閱讀你最後的評論,我覺得你並不需要一個真正的GroupBy,而是一個OrderBy().ThenBy()...

foreach (var el in instances.OrderBy(x => x.PatientID) 
          .ThenBy(x => x.StudyID) 
          .ThenBy(x => x.SeriesID) 
          .ThenBy(x => x.InstanceID)) 
{ 
    // it yields: 
    // Pat1 Std1 Srs1 Inst1 
    // Pat1 Std1 Srs1 Inst2 
    // Pat1 Std1 Srs2 Inst1 
    // Pat1 Std2 Srs2 Inst2 
    // ... 
} 
+0

我認爲這與我目前需要的一樣接近 - 我只是在試圖弄清楚如何遍歷結果。比方說,如果我想將它輸出爲Patient1/Study1/Series1/1,Patient1/Study1/Series1/2等。 – 2011-03-04 22:32:29

+0

閱讀您的評論,我認爲你不需要羣組了,請檢查我的編輯;) – digEmAll 2011-03-04 22:42:54

+0

字典似乎可以滿足我需要的功能 - 它需要一些代碼來實際地遍歷它,一些嵌套的使用KeyValuePairs的foreach,但它完成了工作。我很感激digEmAll。 – 2011-03-04 22:59:31

1

查詢語法中的以下Linq語句應該可以解決您的問題。

var groups = from instance in instances 
         group instance by instance.PatientGuid into patientGroups 
         select new 
         { 
          patientGroups.Key, 
          StudyGroups = from instance in patientGroups 
              group instance by instance.StudyGuid into studyGroups 
              select new 
              { 
              studyGroups.Key, 
              SeriesGroups = from c in studyGroups 
                 group c by c.SeriesGuid into seriesGroups 
                 select seriesGroups 
              } 

         }; 

然後,您可以在組上使用以下一組嵌套的foreach循環來迭代組。這將允許您高效地創建目錄樹並在每個級別執行任何其他操作。

foreach (var patientGroups in groups) 
      { 
       Console.WriteLine("Patient Level = {0}", patientGroups.Key); 
       foreach (var studyGroups in patientGroups.StudyGroups) 
       { 
        Console.WriteLine("Study Level = {0}", studyGroups.Key); 
        foreach (var seriesGroups in studyGroups.SeriesGroups) 
        { 
         Console.WriteLine("Series Level = {0}", seriesGroups.Key); 
         foreach (var instance in seriesGroups) 
         { 
          Console.WriteLine("Instance Level = {0}", instance.InstanceGuid); 
         } 
        } 
       } 

      } 

這是一個概念驗證,但初步測試表明它可以正常工作。任何意見將不勝感激。

1

Eric Lippert完美地解釋瞭如何避免可怕的嵌套,並使用「查詢連續」(into關鍵字)編寫一個單一的查詢。

我認爲你可以多做一步,直接使用GroupBy方法編寫它。有時,使用LINQ方法直接給你更清晰的代碼,我覺得這是一個這樣的例子:

var groups = instances. 
    GroupBy(instance => instance.PatientID). 
    GroupBy(patientGroup => patientGroup.StudyID). 
    GroupBy(studyGroup => studyGroup.SeriesID). 
    GroupBy(seriesGroup => seriesGroup.InstanceID). 
    GroupBy(instanceGroup => patientGroups.Key); 

(我真的不知道這是不是你要找的東西 - 我只是做了「句法轉型」什麼埃裏克寫道 - 我認爲我並沒有改變Eric的查詢的意思)

編輯可能有一些掛羊頭賣狗肉與上次group by,因爲它是不完全規則。