0

Updating the original question as I couldn't explain myself clearly and people focused on XML parsing instead of what I want - sorry

I have a string array which contain strings formatted like this:

A > A1 > A1-1
A > A1 > A1-2
A > A1 > A1-3
A > A2 > A2-1
A > A2 > A2-2
B > B1 > B1-1
C > C1 > C1-1

The strings actually represent a category tree like this:

A
|-- A1
|   |-- A1-1
|   |-- A1-2
|   |-- A1-3
|
|-- A2
|   |-- A2-1
|   |-- A2-2
B
|-- B1
|   |-- B1-1
C
|-- C1
|   |-- C1-1

How can I convert that string array to a collection that actually contains categories and sub categories? I want to convert that string array to MyCategory objects and have them in a list so that I can have a product category tree.

//single category class
public class MyCategory
{
    public string Title {get; set;}
    public IEnumerable<MyCategory> Children {get; set;}
}
Tequilalime
  • 611
  • 9
  • 29
  • what algorithms did you try ? – Shashank Shekhar Mar 24 '15 at 14:42
  • 1
    You should look at XDocument (System.linq.xml) – C1rdec Mar 24 '15 at 14:43
  • Add in the code you're using to read the XML; that's where you need to start. – Brian Mar 24 '15 at 14:43
  • @Brian I've updated the question to include my xml read methods. – Tequilalime Mar 24 '15 at 14:48
  • People seem to be focusing on the XML parts of your input and how you work with XML. Maybe you should delete your question and repost it just showing the text strings that you want to convert to a category structure. – RenniePet Mar 24 '15 at 15:00
  • I think @Alaminut parses the XML just fine and his question is about how to create a Tree object. Correct me if I am wrong. If you are trying to do exactly what I say, you should check this post : http://stackoverflow.com/questions/66893/tree-data-structure-in-c-sharp – Hozikimaru Mar 24 '15 at 15:05
  • 1
    @RenniePet I've updated the original question instead of deleting it. I think it is more understandable now? – Tequilalime Mar 24 '15 at 15:13
  • @SurgeonofDeath yes I want to convert a specially formatted string array to a tree like in your question. The linked question explains tree structure very well but still doesn't help with how to parse my example strings into tree nodes. – Tequilalime Mar 24 '15 at 15:19
  • You're right, it is more courteous to those who have tried to help that you don't delete your question. – RenniePet Mar 24 '15 at 15:26

3 Answers3

0

For the parsing here is an Example

and for the foreach you can use XDocument like so :

var xml = "<root><product><title>Product Title</product><category>A > A1 > A1-1</category></product><product><title>Product Title</product><category>A > A1 > A1-2</category></product><product><title>Product Title</product><category>A > A2 > A2-1</category></product><product><title>Product Title</product><category>B > B1 > B1-1</category></product></root>";
var doc = XDocument.Parse(xml);

var products = doc.Root.Elements("product");

foreach (var product in products)
{
    var title = product.Element("title").Value;
    var category = product.Element("category").Value;
    var categories = category.Replace(" ",string.Empty).Split('>');
    Console.WriteLine (categories);
}

Ouput:

enter image description here

Community
  • 1
  • 1
C1rdec
  • 1,647
  • 1
  • 21
  • 46
  • Thank you for the reply Cedric, have you seen my updated question? I've included the xml reading methods I'm using. My problem is how can I convert category strings to a list of categories with sub categories included. – Tequilalime Mar 24 '15 at 14:55
  • The string array will not have the tree structure @Cedric. – Hozikimaru Mar 24 '15 at 15:06
  • You will need to make a function that take the first index of the string table as the Title and the others would be subCategories – C1rdec Mar 24 '15 at 15:11
  • @Cedric, the original question has been updated. Sorry for misleading but xml is actually irrelevant for my problem. – Tequilalime Mar 24 '15 at 15:14
0

Here's one way of doing it.

   // Single category class
   public class MyCategory
   {
      public string Title { get; set; }
      public Dictionary<string, MyCategory> Children { get; set; }

      // Constructor
      public MyCategory(string title)
      {
         Title = title;
         Children = new Dictionary<string, MyCategory>();
      }
   }


   internal class SO29235482
   {
      // Dictionary for the root nodes
      private readonly Dictionary<string, MyCategory> _categoryTree = 
                                                            new Dictionary<string, MyCategory>();


      public void JustTesting()
      {
         AddCategoryToTree("A > A1 > A1-1");
         AddCategoryToTree("A > A1 > A1-2");
         AddCategoryToTree("A > A1 > A1-3");
         AddCategoryToTree("A > A2 > A2-1");
         AddCategoryToTree("A > A2 > A2-2");
         AddCategoryToTree("B > B1 > B1-1");
         AddCategoryToTree("C > C1 > C1-1");

         if (AddCategoryToTree("C > C1 > C1-1"))
            throw new Exception("Incorrect return value for existing entry.");
      }


      /// <summary>
      /// Method to add (if necessary) a category to the category tree. (No input error checking is 
      /// done - this is simple "proof of concept" code.
      /// </summary>
      /// <param name="textInput">titles separated by '>', for example "A > A1 > A1-1"</param>
      /// <returns>true = category added, false = already in tree</returns>
      public bool AddCategoryToTree(string textInput)
      {
         // Parse the input - no error checking done
         string[] titleArray = textInput.Split('>');

         // Use recursive method to add the nodes to the tree, if not already there
         return AddNodesToTree(titleArray, 0, _categoryTree);
      }


      /// <summary>
      /// Recursive method to process each level in the input string, creating a node if necessary 
      /// and then calling itself to process the next level.
      /// </summary>
      private static bool AddNodesToTree(string[] titleArray, int thisIndex, 
                                         Dictionary<string, MyCategory> priorDictionary)
      {
         if (thisIndex >= titleArray.Length)
            return false;

         bool treeUpdated = false;

         // Create node entry in prior Dictionary if not already there
         string thisTitle = titleArray[thisIndex].Trim();
         MyCategory thisNode;
         if (!priorDictionary.TryGetValue(thisTitle, out thisNode))
         {
            thisNode = new MyCategory(thisTitle);
            priorDictionary.Add(thisTitle, thisNode);
            treeUpdated = true;
         }

         // Process the lower-level nodes using this recursive method
         return AddNodesToTree(titleArray, ++thisIndex, thisNode.Children) | treeUpdated;
      }
   }

I've replaced your IEnumerable with a Dictionary<> since that seemed more natural. But it can be recoded to use IEnumerable and then do a Find() instead of direct Dictionary lookup via key.

RenniePet
  • 11,420
  • 7
  • 80
  • 106
  • Thank you for your help RenniePet, your answer led me to a more suitable solution for my project. I'll post it in a minute. – Tequilalime Mar 25 '15 at 13:12
0

I solved my problem with the solution below. I was so focused on xml part of the service code I couldn't see the actual problem myself as well. Rennie's answer put me on the right track tough.

This is how I get IEnumerable from an array of "A > A1 > A1-1" formatted strings:

private static void RecurseProductCategories(string[] categoryNames, List<Category> parentList, Category parent = null)
        {
            if (categoryNames.Length > 0)
            {
                var catName = categoryNames[0].Trim();
                var catObject = parentList.SingleOrDefault(f => f.Title == catName);
                if (catObject == null)
                {
                    catObject = new Category { Title = catName, Slug = catName.GenerateSlug(), Parent = parent };
                    parentList.Add(catObject);
                }

                RecurseProductCategories(categoryNames.Skip(1).ToArray(), catObject.Children, catObject);
            }
        }

This recursive function gives me what I want when called like this:

string[] demoArray = new string[]
{
    "A > A1 > A1-1",
    "A > A1 > A1-2",
    "A > A2 > A2-1",
    "B > B1"
}

var categoryList = new List<Category>();

for(int i=0; i < demoArray.Length; i++)
{
    string[] categoryStringSplit = demoArray[i].Split('>');
    RecurseProductCategories(categoryStringSplit, categoryList);
}
Tequilalime
  • 611
  • 9
  • 29