2015-09-25 3 views
0

Я пытаюсь преобразовать строку в иерархический список, где каждая строка в строке представляет собой один элемент внутри иерархии.Строка в иерархический список

Например у меня есть следующая строка:

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

Название категории не может включать или -> персонажи.

E.g. следующая строка:

3 - Первый уровень 1> Второй уровень 1> Третий уровень 1

добавить бы категорию в список с идентификатором 3, имя «Третьего Уровня 1» и родитель будет указывать на категорию, где имя «Второй уровень 1» (id = 2 в примере выше, а не id = 6).

Обратите внимание, что может быть несколько категорий с одинаковым именем, поэтому для поиска родителя потребуется искать весь путь.

До сих пор мне удалось разделить строку на строку, а затем для каждой строки я делаю еще одно разделение на дефис, чтобы получить идентификатор и полное название категории. Затем я могу выполнить дальнейший раскол по сравнению с символом, чтобы получить части категории. Я беру последнюю часть, чтобы получить название категории, и если есть более одной части, я знаю, что мне нужно найти родителя.

Это где я заблудиться, как я теперь нужно использовать оставшиеся части, чтобы отработать родителей, принимая во внимание мое выше, что несколько категорий может иметь такое же имя

Я был бы признателен, если кто-то может показать как это можно сделать.Благодаря

ответ

2

Потому что это больше нравится, я сделал свой класс непреложное:

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 
+0

Спасибо, я смог использовать это, чтобы заставить меня двигаться. Мне пришлось создать мод, чтобы ключ словаря и название категории не имели того же значения. Мне не нужно, чтобы имя категории имело полный путь. Кроме того, это работало. – nfplee

+0

@nfplee: Затем вы должны перенести его и/или отметить его как правильный ответ. Рад, что смог помочь. – Oliver

+0

Я как раз собирался. Мне просто пришлось добавить комментарий к @Arghya C, чтобы объяснить, почему я принял ваш ответ. Еще раз спасибо. – nfplee

2

Если я правильно понял вашу формулировку проблемы, этот код должен работать

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

Update

После уточнения, это измененный код

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 
      } 
     } 
     } 
    } 
    } 
] 
+0

Спасибо, но затем после дальнейших испытаний я нашел он выбирает неправильный родитель, если у меня есть повторяющиеся имена. Я добавил еще одну строку к моему вопросу, которую вы можете использовать, чтобы увидеть, что ваш пример не работает. – nfplee

+0

@nfplee ваши категории могут иметь только три уровня или они могут иметь любое количество уровней? как 8 - Первый уровень 2> Второй уровень 1> Третий уровень 1> Уровень 3 уровня> Пятый уровень 2> Шестой уровень 3 ...? –

+0

Неограниченные уровни. На данный момент я играю с идеей от @Oliver, где я буду использовать ключ словаря для полного имени, что значительно упростит поиск. – nfplee