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;}
}
9
  • what algorithms did you try ? Commented Mar 24, 2015 at 14:42
  • 1
    You should look at XDocument (System.linq.xml) Commented Mar 24, 2015 at 14:43
  • Add in the code you're using to read the XML; that's where you need to start. Commented Mar 24, 2015 at 14:43
  • @Brian I've updated the question to include my xml read methods. Commented Mar 24, 2015 at 14:48
  • 1
    @RenniePet I've updated the original question instead of deleting it. I think it is more understandable now? Commented Mar 24, 2015 at 15:13

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

4 Comments

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.
The string array will not have the tree structure @Cedric.
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
@Cedric, the original question has been updated. Sorry for misleading but xml is actually irrelevant for my problem.
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.

1 Comment

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.
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);
}

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.