0
<Nodes>
  <Node>
    <ID>1</ID>
    <TIDS>
      <TID>2</TID>
      <TID>3</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>2</ID>
    <TIDS>
      <TID>4</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>3</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>4</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>5</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>6</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>7</ID>
  </Node>
</Nodes>

i want to write query which will fselect TID and again query xml to select it's TID suppose my where condition is ID equal 1 then i want out put 2,3,4,7 in my where condition if i put ID equal 5 then put put 7 how to write recursive linq

2
  • 2
    Why should (based on what logic) it return 2,3,4 and 7 for ID=1? Commented Jul 25, 2013 at 17:31
  • @Guanxi I believe the behavior OP is looking for is that an ID of 1 returns 2 and 3, and then the lookup continues with those two IDs. So then looking up 2 will yield 4, and looking up 3 will yield 7 (continuing with 4 will also yield 7 and looking up 7 will reveal nothing). In total, having started at 1 the unique IDs the function has 'discovered' will be 2,3,4, and 7. Commented Jul 25, 2013 at 18:10

3 Answers 3

1
 var result = xml.Elements()
   // Find element with <ID>1</ID>
   .Where(x => x.Elements().Any(d => d.Name == "ID" && d.Value == "1"))
   // Find element <TIDS>
   .Elements().Where(x => x.Name == "TIDS")
   // Find elements <TID>
   .Elements().Where(x => x.Name == "TID")
   // Select values
   .Select(x => x.Value);

note, I made the xml variable like this:

   XElement xml = XElement.Parse (@"<Nodes>
  <Node>
    <ID>1</ID>
    <TIDS>
      <TID>2</TID>
      <TID>3</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>2</ID>
    <TIDS>
      <TID>4</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>3</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>4</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>5</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>6</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>7</ID>
  </Node>
</Nodes>");
Sign up to request clarification or add additional context in comments.

2 Comments

@James - Thanks, I'm not sure I got it right - but I think if you put my code in a function and then called it recursively it might be what he wants.
This only returns the TID values for the initial ID. It doesn't query again for each TID.
0

Recursive LINQ is more of a party trick than anything you'd actually want to use in production code, but here's one way to do it:

XElement nodes = /* load your xml */;

Func<XElement, int, IEnumerable<int>> query = null;

/* BAD CODE, DO NOT USE! */
query = (x,id) => 
   x.Elements("Node")
     .Where (node => (int)node.Element("ID") == id) // find our node
     .Descendants("TID")      // this will be empty in the base case
     .Select (tid => (int)tid)
     .SelectMany(tid => 
       query(x,tid)           // recurse
       .Concat(new[]{tid})    // keep the current TID if its node had no TIDs
     )
     .Distinct();

var resultOf7423 = query(nodes, 1);
var resultOf7 = query(nodes, 5);
/* END BAD CODE (I HOPE) */

This is baffling and fragile code, and you almost certainly shouldn't use it. Instead, you could create an extension method off of XElement:

public static IEnumerable<int> SelectRelatedIds(this XElement element, int id)
{
    if(element == null) throw new ArgumentNullException("element");
    return SelectRelatedIdsIterator(element, id)
                .Where(i => i != id)
                .Distinct();
}

private static IEnumerable<int> SelectRelatedIdsIterator(XElement element, int id)
{
    yield return id;

    var tids = element.Elements("Node")
                .Where (node => (int)node.Element("ID") == id)
                .Descendants("TID");

    foreach (int tid in tids)
        foreach(var i in  SelectRelatedIdsIterator(element, tid))
            yield return i;
}

This keeps the recursion contained and pretty easy to understand (at least once you get your head around iterator blocks). It's also LINQ-friendly and potentially composable in a way that the first way is not.

Comments

0

The function GetTids returns a list of TIDs for the ID you pass in. After the initial ID is passed, it returns a list (tidIndex). Then a loop is run, and each TID value is used to query the XML and is added to the tidList.

List<string> TidList()
{
    var xml = XDocument.Load(@"C:\PathToXml\File.xml");

    var tidIndex = GetTids(xml, "1").ToList();
    var tidList = new List<string>(tidIndex);

    foreach (var tid in tidIndex)
        tidList.AddRange(GetTids(xml, tid));

    return tidList;
}

static IEnumerable<string> GetTids (XDocument xml, string id)
{
    return xml.Descendants("Node")
        .Where(x => x.Element("ID").Value == id)
        .Descendants("TID")
        .Select (s => s.Value);
}

This returns:

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.