字符串到层次列表

String to Hierarchical List

我正在尝试将字符串转换为分层列表,其中字符串中的每一行代表层次结构中的单个项目。

例如我有以下字符串:

1 - First Level 1
2 - First Level 1 > Second Level 1
3 - First Level 1 > Second Level 1 > Third Level 1
4 - First Level 1 > Second Level 2
5 - First Level 2
6 - First Level 2 > Second Level 1
7 - First Level 2 > Second Level 1 > Third Level 1
...

我需要将其转换为以下类型的列表:

public class Category {
    public int Id { get; set; }
    public string Name { get; set; }
    public Category Parent { get; set; }
}

类别名称不能包含 - 或 > 字符。

例如以下行:

3 - First Level 1 > Second Level 1 > Third Level 1

将向列表中添加一个类别,其 id 为 3,名称为 "Third Level 1",Parent 将指向名称为 "Second Level 1" 的类别(示例中的 id = 2以上而不是 id = 6).

请注意,可能有多个同名类别,因此需要查找整个路径才能获得父类别。

到目前为止,我已经成功地按行拆分字符串,然后针对每一行我对连字符进行另一次拆分以获取 ID 和完整的类别名称。然后我可以对大于符号进行进一步拆分以检索类别部分。我用最后一部分来获取类别名称,如果有多个部分,我知道我需要查找父级。

这是我迷路的地方,因为我现在需要使用剩余的部分来计算父项,考虑到我在上面考虑的多个类别可能具有相同的名称

如果有人能告诉我如何做到这一点,我将不胜感激。谢谢

如果我正确理解了你的问题陈述,这段代码应该可以工作

var strings = File.ReadAllLines(@"C:\YourDirectory\categories.txt");

var categories = new List<Category>();

foreach (var line in strings)
{
    var category = new Category(); //line = 3 - First Level 1 -> Second Level 1 -> Third Level 1
    var cats = line.Split('>').ToList(); //3 - First Level 1, Second Level 1, Third Level 1
    category.Id = int.Parse(cats.First().Split('-').First().Trim()); //3

    if (cats.Count > 1)
    {
        category.Name = cats.Last().Trim(); //Third Level 1
        var parentStr = cats.ElementAt(cats.Count - 2).Trim();
        if (parentStr.Contains('-'))
            parentStr = parentStr.Split('-').Last().Trim();
        category.Parent = categories.FirstOrDefault(c => c.Name == parentStr);
    }
    else
        category.Name = cats.First().Split('-').Last().Trim(); //for 1 - First Level 1

    categories.Add(category);
}

更新

经过澄清,这是修改后的代码

var lines = File.ReadAllLines(@"C:\YourDirectory\categories.txt");
var lookup = new List<KeyValuePair<List<string>, Category>>(); //key = parents in order

foreach (var line in lines)
{
    var category = new Category (); //line = 3 - First Level 1 -> Second Level 1 -> Third Level 1
    var parts = line.Split('>').ToList(); //3 - First Level 1, Second Level 1, Third Level 1
    category.Id = int.Parse(parts.First().Split('-').First().Trim()); //3

    if (parts.Count > 1) //has parent
    {
        category.Name = parts.Last().Trim(); //Third Level 1
        if (parts.Count == 2) //has one level parent
        {
            var parentStr = parts.First().Split('-').Last().Trim();
            if (lookup.Any(l => l.Value.Parent == null && l.Value.Name == parentStr))
            {
                var parent = lookup.First(l => l.Value.Parent == null && l.Value.Name == parentStr);
                category.Parent = parent.Value;
                lookup.Add(new KeyValuePair<List<string>,Category>(new List<string> { parent.Value.Name }, category));
            }
        }
        else //has multi level parent
        {
            var higherAncestors = parts.Take(parts.Count - 2).Select(a => a.Split('-').Last().Trim()).ToList(); //.GetRange(1, parts.Count - 2).Select(a => a.Trim()).ToList();
            var parentStr = parts.Skip(parts.Count - 2).First().Trim();
            if (lookup.Any(l => MatchAncestors(l.Key, higherAncestors) && l.Value.Name == parentStr))
            {
                var parent = lookup.First(l => MatchAncestors(l.Key, higherAncestors) && l.Value.Name == parentStr);
                category.Parent = parent.Value;
                var ancestors = parent.Key.ToList();
                ancestors.Add(parent.Value.Name);
                lookup.Add(new KeyValuePair<List<string>, Category>(ancestors, category));
            }
        }
    }
    else //no parent
    {
        category.Name = parts.First().Split('-').Last().Trim(); //for 1 - First Level 1
        lookup.Add(new KeyValuePair<List<string>,Category> (new List<string>(), category));
    }
}

var categories = lookup.Select(l => l.Value); //THIS IS YOUR RESULT

private static bool MatchAncestors(List<string> ancestors1, List<string> ancestors2)
{
    if (ancestors1.Count != ancestors2.Count)
        return false;
    for (int i = 0; i < ancestors1.Count; i++)
    {
        if (ancestors1[i] != ancestors2[i])
            return false;
    }
    return true;
}

对于这个测试数据:

1 - First Level 1
2 - First Level 1 > Second Level 1
3 - First Level 1 > Second Level 1 > Third Level 1
4 - First Level 1 > Second Level 2
5 - First Level 2
6 - First Level 2 > Second Level 1
7 - First Level 2 > Second Level 1 > Third Level 1
8 - First Level 2 > Second Level 1 > Third Level 1 > Fourth Level 1
9 - First Level 1 > Second Level 1 > Third Level 1 > Fourth Level 2

这是查找值(如json):

[
  {
    "Key": [],
    "Value": {
      "Id": 1,
      "Name": "First Level 1",
      "Parent": null
    }
  },
  {
    "Key": ["First Level 1"],
    "Value": {
      "Id": 2,
      "Name": "Second Level 1",
      "Parent": {
        "Id": 1,
        "Name": "First Level 1",
        "Parent": null
      }
    }
  },
  {
    "Key": ["First Level 1","Second Level 1"],
    "Value": {
      "Id": 3,
      "Name": "Third Level 1",
      "Parent": {
        "Id": 2,
        "Name": "Second Level 1",
        "Parent": {
          "Id": 1,
          "Name": "First Level 1",
          "Parent": null
        }
      }
    }
  },
  {
    "Key": ["First Level 1"],
    "Value": {
      "Id": 4,
      "Name": "Second Level 2",
      "Parent": {
        "Id": 1,
        "Name": "First Level 1",
        "Parent": null
      }
    }
  },
  {
    "Key": [],
    "Value": {
      "Id": 5,
      "Name": "First Level 2",
      "Parent": null
    }
  },
  {
    "Key": ["First Level 2"],
    "Value": {
      "Id": 6,
      "Name": "Second Level 1",
      "Parent": {
        "Id": 5,
        "Name": "First Level 2",
        "Parent": null
      }
    }
  },
  {
    "Key": ["First Level 2","Second Level 1"],
    "Value": {
      "Id": 7,
      "Name": "Third Level 1",
      "Parent": {
        "Id": 6,
        "Name": "Second Level 1",
        "Parent": {
          "Id": 5,
          "Name": "First Level 2",
          "Parent": null
        }
      }
    }
  },
  {
    "Key": ["First Level 2","Second Level 1","Third Level 1"],
    "Value": {
      "Id": 8,
      "Name": "Fourth Level 1",
      "Parent": {
        "Id": 7,
        "Name": "Third Level 1",
        "Parent": {
          "Id": 6,
          "Name": "Second Level 1",
          "Parent": {
            "Id": 5,
            "Name": "First Level 2",
            "Parent": null
          }
        }
      }
    }
  },
  {
    "Key": ["First Level 1","Second Level 1","Third Level 1"],
    "Value": {
      "Id": 9,
      "Name": "Fourth Level 2",
      "Parent": {
        "Id": 3,
        "Name": "Third Level 1",
        "Parent": {
          "Id": 2,
          "Name": "Second Level 1",
          "Parent": {
            "Id": 1,
            "Name": "First Level 1",
            "Parent": null
          }
        }
      }
    }
  }
]

因为我更喜欢它,所以我让你的 class 不可变:

public class Category
{
    public int Id { get; private set; }
    public string Name { get; private set; }
    public Category Parent { get; private set; }

    public Category(int id, string name, Category parent)
    {
        Id = id;
        Name = name;
        Parent = parent;
    }

    public override string ToString()
    {
        return Id + " " + Name
            + (Parent == null ? String.Empty : (Environment.NewLine + "   Parent: " + Parent));
    }
}

通过使用这段代码,我得到了所有可用类别的简单列表,其中每个类别都获得了对其父类别的引用:

var categories = new Dictionary<String, Category>(StringComparer.InvariantCultureIgnoreCase);

using (var reader = new StringReader(_SampleData))
{
    string line;

    while ((line = reader.ReadLine()) != null)
    {
        if (String.IsNullOrWhiteSpace(line))
            continue;

        var elements = line.Split('-');
        var id = int.Parse(elements[0]);
        var name = elements[1].Trim();
        var index = name.LastIndexOf('>');
        Category parent = null;

        if (index >= 0)
        {
            var parentName = name.Substring(0, index).Trim();
            categories.TryGetValue(parentName, out parent);
        }

        var category = new Category(id, name, parent);
        categories.Add(category.Name, category);
    }
}

仅用于可视化调用:

foreach (var item in categories.Values)
{
    Console.WriteLine(item);
}

输出将是:

1 First Level 1
2 First Level 1 > Second Level 1
   Parent: 1 First Level 1
3 First Level 1 > Second Level 1 > Third Level 1
   Parent: 2 First Level 1 > Second Level 1
   Parent: 1 First Level 1
4 First Level 1 > Second Level 2
   Parent: 1 First Level 1
5 First Level 2
6 First Level 2 > Second Level 1
   Parent: 5 First Level 2
7 First Level 2 > Second Level 1 > Third Level 1
   Parent: 6 First Level 2 > Second Level 1
   Parent: 5 First Level 2