2011-03-04 168 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,因为它是不完全规则。