Home > Blockchain >  How to struct an object to represent a list of topics?
How to struct an object to represent a list of topics?

Time:04-21

I'm coding at C# and I'm trying to make an OOP representation of a list of topics. I tried countless approaches but I still not being able to reach the desired result.

I want to make a method later that will output it like:

1) Text
  1.1) Text
2) Text
  2.1) Text
  2.2) Text
    2.2.1) Text
    2.2.2) Text
  2.3) Text
3) Text
  3.1) Text

When needed to get a single topic, I would like to create a method calling my object like:

private string GetSingleTopic()
{
  return $"{Topic.Numerator}) {Topic.Text}"
}

EXAMPLES

Example 1

I would be able to instantiate the object such as:

var variable = new TopicObject
{
  "TitleA",
  "TitleB",
  "TitleC" 
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
   --- OUTPUT --- */

Example 2

Be able to instantiate the object such as:

var variable = new TopicObject
{
  "TitleA",
  "TitleB",
  "TitleC":
  {
    "TitleD":
    {
      "TitleE"
    },
    "TitleF":
    {
      "TitleG",
      "TitleH"
    }
  }
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
  3.1) TitleD
    3.1.2) TitleE
  3.2) TitleF
    3.2.1) TitleG
    3.2.2) TitleH
   --- OUTPUT --- */

My Approach

This, was one of my many approaches. I couldn't use it because of many reasons but the structure is pretty similar to what I want to achieve and I put here as an example.

public abstract class TopicBase
{
    public List<Topic> Topics { get; set; } // optional

    protected TopicBase() { Topics = new List<Topic>(); }
    protected TopicBase(List<Topic> topics) { Topics = topics; }

    public TopicBase AddTopic(string topicText)
    {
        var test = new Topic(topicText);
        Topics.Add(test);
        return this;
    }
}

public class Topic
{
    public Topic(string text)
    {
        Numerator  ;
        Text = text;
    }
    public int Numerator { get; }
    public string Text { get; }
}

public class TopicLevel1 : TopicBase { }
public class TopicLevel2 : TopicBase { }
public class TopicLevel3 : TopicBase { }

CodePudding user response:

Let's start by defining a data structure that can hold the topics:

public class Topics<T> : List<Topic<T>> { }

public class Topic<T> : List<Topic<T>>
{
    public T Value { get; private set; }
    public Topic(T value, params Topic<T>[] children)
    {
        this.Value = value;
        if (children != null)
            this.AddRange(children);
    }
}

That allows us to write this code:

var topics = new Topics<string>()
{
  new Topic<string>("TitleA"),
  new Topic<string>("TitleB"),
  new Topic<string>("TitleC",
    new Topic<string>("TitleD",
        new Topic<string>("TitleE")),
    new Topic<string>("TitleF",
        new Topic<string>("TitleF"),
        new Topic<string>("TitleH")))
};

That matches your "Example 2" data.

To output the result we add two ToOutput methods.

To Topics<T>:

public IEnumerable<string> ToOutput(Func<T, string> format)
    => this.SelectMany((t, n) => t.ToOutput(0, $"{n   1}", format));

To Topic<T>:

public IEnumerable<string> ToOutput(int depth, string prefix, Func<T, string> format)
{
    yield return $"{new string(' ', depth)}{prefix}) {format(this.Value)}";
    foreach (var child in this.SelectMany((t, n) => t.ToOutput(depth   1, $"{prefix}.{n   1}", format)))
    {
        yield return child;
    }
}

Now I can run this code:

foreach (var line in topics.ToOutput(x => x))
{
    Console.WriteLine(line);
}

That gives me:

1) TitleA
2) TitleB
3) TitleC
 3.1) TitleD
  3.1.1) TitleE
 3.2) TitleF
  3.2.1) TitleF
  3.2.2) TitleH

CodePudding user response:

If the goal is to have some structure that will help with the output of the topic hierarchy, you already have it (and may even be able to simplify it more).

For example, here's an almost-minimal Topic to get what you want:

public class Topic
{
    public string Title { get; set; }
    public List<Topic> SubTopics { get; private set; } = new();

    public Topic() : this("DocRoot") { }

    public Topic(string title) => Title = title;

    public void AddTopics(List<Topic> subTopics) => SubTopics.AddRange(subTopics);

    public void AddTopics(params Topic[] subTopics) => SubTopics.AddRange(subTopics);

    public override string ToString() => Title;
}

That is, you have a Topic that can have SubTopics (aka children) and that's all you need.

With that, we can build your second example:

var firstLevelTopics = new List<Topic>();
for (var c = 'A'; c < 'D';   c)
{
    firstLevelTopics.Add(new Topic(c.ToString()));
}

var cTopic = firstLevelTopics.Last();
cTopic.AddTopics(
    new Topic
    {
        Title = "D",
        SubTopics = { new Topic("E") }
    },
    new Topic
    {
        Title = "F",
        SubTopics = { new Topic("G"), new Topic("H") }
    });

Now, imagine if we had a function to print the hierarchy from the list of top-level topics. I'm leaving the final detail for yourself in case this is homework.

PrintTopics(firstLevelTopics);
static void PrintTopics(List<Topic> topics, string prefix = "")
{
    // For the simple case, we can just loop and print...
    for (var i = 0; i < topics.Count;   i)
    {
        var topic = topics[i];
        var level = i   1;
        Console.WriteLine($"{prefix}{level}) {topic}");
        // ...but, if we want to print the children, we need more.
        // Make a recursive call to print the SubTopics
        // PrintTopics(<What goes here?>);
    }
}
  • Related