tags:

views:

520

answers:

5

I have a following items displayed on an asp.net page (C#).

1 Welcome
2 Whats New
2.1 Gifts
2.2 Ideas
2.3 Others
2.3.1 Novelty
2.3.2 Boats
2.4 Vehicals
2.5 Fruits

Now the user can remove any child items ( not root for ex user can remove item 2.1 gifts or item 2.3.1 novelty) Once user remove the item, i need to re-nunmber the structure using C#. I am looking for any suggestion / ideas / code to accomplish this task.

1 Welcome
2 Whats New
2.1 Gifts(remove)
2.2 Ideas
2.3 Others
2.3.1 Novelty(remove)
2.3.2 Boats
2.4 Vehicals
2.5 Fruits

Result should be -

1 Welcome
2 Whats New
2.1 Ideas
2.2 Others
2.2.1 Novelty(remove)
2.2.2 Boats
2.3 Vehicals
2.4 Fruits

+1  A: 

I suggest the use of multidimensional collections. Thus, when you remove the first item in the "2" collection (2.1 - Gifts), "Ideas" would automatically become the new first item in that collection, and would from then on be read as 2.1 instead of 2.2

But since the "1" collection only has one item in it, it wouldn't be listed as 1.1
This is something you'll have to look out for ...

Perhaps I should elaborate:
You'd have a List "L1" that would contain first-level items.
It would look like this:

List L1[2]
  List Welcome[0]
  List What's New[5]
    List Gifts[0]
    List Ideas[0]
    List Others[2]
      List Novelty[0]
      List Boats[0]
    List Vehicles[0]
    List Fruits[0]

I should add that the brackets in this example represent the length of each list, not an array.

One of the easiest ways to achieve this structure is to create an "Item" class that contains a String for the ItemName as well as a List of child Items. That way each Item has the potential of having children, or having no children.

Giffyguy
+5  A: 

Is the number purely "optical" sugar for the user? If yes, I'd number them on the fly while writing the output, without keeping the number stored anywhere.

Lucero
This is an interesting idea as well. It makes for a simpler solution.
Giffyguy
...and even if you want to use references or whatever you may also dynamically create the numbering whenever you need it, associating the original items with the numbers in a dictionary. And it also allows to use different numbering schemes easily, such as letters or roman numbers. ;)
Lucero
+1 exactly what I was going to say. Also this extra work will likely be immeasurably fast compared to the other stuff happening to generate the page.
Daniel Earwicker
+2  A: 

Not a very smart solution, but the following code renumbers a list of lines given as input. It splits each line at the space separating the numbering from the rest, determines the nesting level by counting the number of dots in the numbering, and uses this information together with the nesting level of the last line to update a list with the current numbering for each nesting level. Finally the current numbering stored in the list is joined with the text and outputed.

List<Int32> numbering = new List<Int32>();

Int32 lastNestingLevel = -1;

foreach (String line in lines)
{
    String[] parts = line.Split(new Char[] { ' ' }, 2);

    Int32 currentNestingLevel = parts[0].Count(c => c == '.');

    if (currentNestingLevel > lastNestingLevel)
    {
        // Start a new nesting level with number one.
        numbering.Add(1);
    }
    else if (currentNestingLevel == lastNestingLevel)
    {
         // Increment the number of the current nesting level.
        numbering[currentNestingLevel] += 1;        }
    else if (currentNestingLevel < lastNestingLevel)
    {
         // Remove the deepest nesting level...
        numbering.RemoveAt(numbering.Count - 1);
         // ...and increment the numbering of the current nesting level.
        numbering[currentNestingLevel] += 1;
    }

    lastNestingLevel = currentNestingLevel;

    String newNumbering = String.Join(".", numbering
        .Select(n => n.ToString())
        .ToArray());

    Console.WriteLine(newNumbering + " " + parts[1]);
}

For the following input

List<String> lines = new List<String>()
{
    "1 Welcome",
    "2 Whats New",
    //"2.1 Gifts",
    "2.2 Ideas",
    "2.3 Others",
    //"2.3.1 Novelty",
    "2.3.2 Boats",
    "2.4 Vehicals",
    "2.5 Fruits"
};

the output is the following.

1 Welcome
2 Whats New
2.1 Ideas
2.2 Others
2.2.1 Boats
2.3 Vehicals
2.4 Fruits

UPDATE

Here is a variant using a dictionary - it simplifies the update, but complicates the generation of the new numbering a bit.

Dictionary<Int32, Int32> numbering = new Dictionary<Int32, Int32>();

Int32 lastNestingLevel = -1;

foreach (String line in lines)
{
    String[] parts = line.Split(new Char[] { ' ' }, 2);

    Int32 currentNestingLevel = parts[0].Count(c => c == '.');

    if (currentNestingLevel > lastNestingLevel)
    {
        numbering[currentNestingLevel] = 1;
    }
    else
    {
        numbering[currentNestingLevel] += 1;
    }

    lastNestingLevel = currentNestingLevel;

    String newNumbering = String.Join(".", numbering
        .Where(n => n.Key <= currentNestingLevel)
        .OrderBy(n => n.Key)
        .Select(n => n.Value.ToString())
        .ToArray());

    Console.WriteLine(newNumbering + " " + parts[1]);
}

This variant also fixes a bug in the first version. If the nesting level drops by more than one at once, the first variant will produce false outputs.

2.3.2.1 Vehicals
2.5 Fruits <= nesting level drops by two

I assume an input where the nesting level raises by more than one at once is not valid or at least a more precise definition how to handle this case is required, but both variants may have problems with that kind of inputs.

Daniel Brückner
Thanks a lot to Daniel.Very Very nice and simple logic to re-order list of items. Although thanks to all for their suggestions..!
bugBurger
+1  A: 

Another suggestion: if the data is structured XML, you could use a XSL transformation to do the numbering for you...

Lucero
+1  A: 

I came up with a class to hold the contents:

public class Content
{
    public string Title { get; set; }
    public IList<Content> SubContent
    {
        get;
        private set;
    }

    private Content()
    {
    }

    public Content(string title)
    {
        Title = title;
        SubContent = new List<Content>();
    }
}

Then a recursive function to number/display the contents:

    private static void Display(string marker, IList<Content> content)
    {
        int count = 0;
        foreach (Content c in content)
        {
            string label = marker + (marker.Length > 0 ? "." : "") + (++count);
            Console.WriteLine(label + " " + c.Title);
            if (c.SubContent.Count > 0)
                Display(label, c.SubContent);
        }
    }
CAbbott