How do I query a nested collection using LINQ

drawalegna
drawalegna used Ask the Experts™
on
I have a collection of nested collections which can contain an unlimted number of nested collections. A code snipet below shows the class structure. I need to query the collection by ID to find a match. Sometime I need the Object where the ID was found and at others I need the parent.  I'm new to LINQ and cannot determine how to make the query traverse all levels of the collection. Can someone assist me with this query?

Also given this basic query, I cannot actually query s.Values because s is the base class which does not contain the Values collection. I assume I can type cast the query, but can you address this in the query as well?
The basic query
var Segment = from s in Segments
              where s.ID == 4 && s.Values[2]=='OB'
              select s;


Thanks
class FileDocument
    {
    
        public List<Section> Segments = new List<Section>();
 
        public class Section
        {
            public int ID;
            public string Name;
        }
        public class FileSegment : Section
        {
            public List<string> Values = new List<string>();
        }         
        public class FileLoopGroup : Section
        {
            public List<FileLoop> LoopGroup = new List<FileLoop>(); 
        }
        public class FileLoop
        {
            public List<Section> Sections= new List<Section>();
        }
   }
 
Example:
 
Segments
   FileSegment ID=1
   FileSegment ID=2
   FileLoopGroup ID=3
      FileLoop
         FileSegment ID=4 Values[2]="OB"
         FileSegment ID=5
         FileLoopGroup ID=6
            FileLoop
               FileSegment ID=7
               FileSegment ID=8
      FileLoop
         FileSegment ID=4 Values[2]="ST"
         FileSegment ID=5
   FileSegment ID=9			
 
 
Query 1 needs to return the FileSegment where the match was found.
For example: where s.ID == 4 && s.Values[2]=='OB'would return the first FileSegment
 
Query 2 needs to return the FileLoop that contains the matching Segment.
For example: where s.ID == 4 && s.Values[2]=='ST'would return the second FileLoop

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
You  shoul add an extension method to your project ( meaning a static class containing the two methods below.

Then you can use linq to query all segments in your FileDocument. I provided you with the extension methods and the linq and lambda queries.

To get the parent of a segment, I recommend to adjust the section class to have a parent property which will be set in the constructor. That way you can retrieve the parent just by accessing the Parent property, not having to go through the trouble of writing a linq query for that as well.
// extension methods: 
 public static class LinqExtensions
    {
        public static IEnumerable<FileDocument.FileSegment> GetAllSegments(this FileDocument document)
        {
            foreach (var sgm in document.Segments.GetAllSegments())
                yield return sgm;
        }
 
        public static IEnumerable<FileDocument.FileSegment> GetAllSegments(this List<FileDocument.Section> sections)
        {
            foreach (var sgm in sections)
            {
                if (sgm.GetType() == typeof(FileDocument.FileSegment))
                    yield return (FileDocument.FileSegment)sgm;
                else
                {
                    // segment is a file loop group.
                    var group = (FileDocument.FileLoopGroup)sgm;
 
                    foreach (var loop in group.LoopGroup)
                    {
                        var loopSections = loop.Sections;
                        var loopSegments = loopSections.GetAllSegments().ToList();
 
                        foreach (var loopSegm in loopSegments)
                            yield return loopSegm;
                    }
                }
            }
        }
    }
 
// queries
var segments = fd.GetAllSegments();
 
            // linq
            var searchSegment = from s in segments
                                where s.Values.Count() > 2 &&
                                      s.Values[2] == "OB"
                                select s;
 
            // lambda
            var result = segments.Where(s => s.Values.Count() > 2 && s.Values[2] == "OB"); 
 
            var l = searchSegment.ToList();
            var l2 = result.ToList();

Open in new window

Author

Commented:
Worked like a charm. Thanks so much!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial