5 Replies Latest reply on Jun 22, 2016 6:16 PM by ernstamort

    Get all Parents recursively

    Dries.Verhees

      Hi all,

       

      I have a list of parent elements (at different levels). Starting from a child element and moving upwards, I would like to find the first match.

      Let me illustrate it with an example. Let's assume I have the following element structure:

      sample_tree.png

      My list of parent elements is {Root, 1C, 2A, 3D, 3E}. My algorithm should have the following results:

       

      InputOutput
      3A, 3B, 2A2A
      3D3D
      4A, 4B, 4C, 3E3E
      2D, 2E, 1C1C
      2B, 2C, 1A, 1B, RootRoot

       

      My current implementation does some looping from child to root

       

      var element = childElement;
      for (int levelIteration = 0; levelIteration < 25; levelIteration++)

      {

          if (parentElements.Contains(element))

          {

                return element;

          }

          element = element.Parent;

          if (element == null)

          {

                return null;

          }

      }

      return null;

       

       

       

      Is there a more efficient way to do it?

        • Re: Get all Parents recursively
          gregor

          Hello Dries,

           

          You confused me with the description of your AF structure and with the expectation that when seeking for a parent element by element the search would return the element itself. To my understanding the definition of parent child relationships doesn't allow a constellation where a child is it's own parent.

           

          Luckily the answer to your question is way easier than expected because a brave child knows who its parent is.

          Each AFElement has Parent property which is of type AFElement except it's null but in that case the IsRoot property would return true. Now I am really wondering if anybody is able to follow so let's illustrate with a code sample.

           

          using System;
          using OSIsoft.AF;
          using OSIsoft.AF.Asset;
          
          namespace Whoisparent
          {
              class Program
              {
                  static void Main(string[] args)
                  {
                      PISystem sys = new PISystems()["AFServer"];
                      AFDatabase db = sys.Databases["Nu Green"];
                      AFNamedCollection<AFElement> allElements = AFElement.FindElements(db, null, "", AFSearchField.Name, true, AFSortField.Name, AFSortOrder.Ascending, 10000);
                      foreach (AFElement element in allElements)
                      {
                          if (element.IsRoot)
                          {
                              Console.ForegroundColor = ConsoleColor.Yellow;
                              Console.WriteLine("{0} states to be root.", element.Name);
                          }
                          else
                          {
                              Console.ForegroundColor = ConsoleColor.Green;
                              Console.WriteLine("{0} is parent of {1}", element.Parent.Name, element.Name);
                          }
                      }
                      Console.ReadKey();
                  }
              }
          }
          
            • Re: Get all Parents recursively
              Dries.Verhees

              Hi Gregor,

               

              Thanks for the answer.

              I'm not exactly looking for the parents. I have a list of elements which I would like to match.

              Starting from a given element, I would like to match it against the list of elements.

              If the list does not contain the given element, then check if the parent is member of the list. If not, then move to the parent, etc...

              So it is a recursive pattern, where the element is check against a condition (=list contains). If the condition is not met, then move to the parent.

               

              I noticed that you're code is doing the same as mine, so I expect that my implementation is efficient enough. I was just wondering if there is a more efficient way to search for these matches.

                • Re: Get all Parents recursively
                  gregor

                  Dries Verhees wrote:

                   

                  I noticed that you're code is doing the same as mine, so I expect that my implementation is efficient enough. I was just wondering if there is a more efficient way to search for these matches.

                  You are right! I should have taken a closer look. Sorry!

                  I wouldn't know a way to do this more efficient.

              • Re: Get all Parents recursively
                Rick Davin

                The real performance efficiency to realize is if you've issued an AFElement.LoadElements call to load all pertinent elements.  This will minimize the trips to the server.  But that would have to include every element.Parent.

                 

                As for the code efficiency, you've locked yourself to 25 levels.  Probably more than okay with this, and many would think 25 levels are too many.  Still you could try recursion to omit the magic number of 25:

                 

                public static AFElement FindCustomMatch(this IList<AFElement> parentElements, AFElement element)
                {
                    if (element == null) { return null; }
                    if (parentElements.Contains(element)) { return element; }
                    return FindCustomMatch(parentElements, element.Parent);
                }
                

                 

                You can make it an extension method (as my example) or just a private method.

                2 of 2 people found this helpful
                • Re: Get all Parents recursively
                  ernstamort

                  Here is the non recursive version:

                   

                          public static AFElement FindCustomMatchNonRecursive(this IList<AFElement> parentElements, AFElement element)
                          {
                              if (element == null) { return null; }
                              Stack<AFElement> stack=new Stack<AFElement>();
                              stack.Push(element);
                              while (stack.Count > 0)
                              {
                                  AFElement currentElement = stack.Pop();
                                  if (parentElements.Contains(currentElement)) { return currentElement; }
                                  if(currentElement.Parent!=null) stack.Push(currentElement.Parent);
                              }
                              return null;
                          }
                  
                  2 of 2 people found this helpful