Я надеюсь, что кто-то может помочь мне с этим ...
Допустим, у меня есть этот базовый класс закладок c:
public class FlatBookmark
{
public int PageIndex { get; set; }
public string Path { get; set; }
}
И "плоский" список закладок:
List<FlatBookmark> flatBookmarks = new List<FlatBookmark>()
{
new FlatBookmark() { Path = "Category 1||Page 1", PageIndex = 0 },
new FlatBookmark() { Path = "Category 1||Page 1||Attachment 1", PageIndex = 1 },
new FlatBookmark() { Path = "Category 1||Page 1||Attachment 2", PageIndex = 2 },
new FlatBookmark() { Path = "Category 1||Page 2", PageIndex = 3 },
new FlatBookmark() { Path = "Category 1||Page 2", PageIndex = 4 }, // Ignore (path already exists)
new FlatBookmark() { Path = "Category 1||Page 2", PageIndex = 5 }, // Ignore (path already exists)
new FlatBookmark() { Path = "Category 1||Page 3", PageIndex = 6 },
new FlatBookmark() { Path = "", PageIndex = 123 }, // Empty or null paths should be completely ignored
new FlatBookmark() { Path = null, PageIndex = 321 }, // Empty or null paths should be completely ignored
new FlatBookmark() { Path = "Category 2||Page 1", PageIndex = 7 },
new FlatBookmark() { Path = "Category 2||Page 2", PageIndex = 8 },
new FlatBookmark() { Path = "Category 1||Page 1||Attachment 1", PageIndex = 9 }, // Create a new 'Category1' root, because it is separated by the previous one
new FlatBookmark() { Path = "Category 1||Page 1||Attachment 1", PageIndex = 10 }, // Ignore (path already exists)
new FlatBookmark() { Path = "Category 1||Page 1||Attachment 2", PageIndex = 11 },
};
Теперь я хочу заполнить новые List<Bookmark>
вложенными закладками, разбитыми на любой заданной строке пути, и где заголовок становится последняя часть Пути.
public class Bookmark
{
public int PageIndex { get; set; }
public string Title { get; set; }
public List<Bookmark> Bookmarks; // Nested children
}
Примерно так:
Category 1: PageIndex=0
Page 1: PageIndex=0
Attachment 1: PageIndex=1
Attachment 2: PageIndex=2
Page 2: PageIndex=3
Page 3: PageIndex=6
Category 2: PageIndex=7
Page 1: PageIndex=7
Page 2: PageIndex=8
Category 1: PageIndex=9
Page 1: PageIndex=9
Attachment 1: PageIndex=9
Attachment 2: PageIndex=11
Я создал для него Юнит-тест:
// Category 1
Assert.AreEqual("Category 1", bookmarks[0].Title);
Assert.AreEqual(0, bookmarks[0].PageIndex);
Assert.AreEqual("Page 1", bookmarks[0].Bookmarks[0].Title);
Assert.AreEqual(0, bookmarks[0].Bookmarks[0].PageIndex);
Assert.AreEqual("Attachment 1", bookmarks[0].Bookmarks[0].Bookmarks[0].Title);
Assert.AreEqual(1, bookmarks[0].Bookmarks[0].Bookmarks[0].PageIndex);
Assert.AreEqual("Attachment 2", bookmarks[0].Bookmarks[0].Bookmarks[1].Title);
Assert.AreEqual(2, bookmarks[0].Bookmarks[0].Bookmarks[1].PageIndex);
Assert.AreEqual("Page 2", bookmarks[0].Bookmarks[1].Title);
Assert.AreEqual(3, bookmarks[0].Bookmarks[1].PageIndex);
Assert.AreEqual("Page 3", bookmarks[0].Bookmarks[2].Title);
Assert.AreEqual(6, bookmarks[0].Bookmarks[2].PageIndex);
// Category 2
Assert.AreEqual("Category 2", bookmarks[1].Title);
Assert.AreEqual(7, bookmarks[1].PageIndex);
Assert.AreEqual("Page 1", bookmarks[1].Bookmarks[0].Title);
Assert.AreEqual(7, bookmarks[1].Bookmarks[0].PageIndex);
Assert.AreEqual("Page 2", bookmarks[1].Bookmarks[1].Title);
Assert.AreEqual(8, bookmarks[1].Bookmarks[1].PageIndex);
// Category 1 again (not combined with the first one, because there was another category in between)
Assert.AreEqual("Category 1", bookmarks[2].Title);
Assert.AreEqual(9, bookmarks[2].PageIndex);
Assert.AreEqual("Page 1", bookmarks[2].Bookmarks[0].Title);
Assert.AreEqual(9, bookmarks[2].Bookmarks[0].PageIndex);
Assert.AreEqual("Attachment 1", bookmarks[2].Bookmarks[0].Bookmarks[0].Title);
Assert.AreEqual(9, bookmarks[2].Bookmarks[0].Bookmarks[0].PageIndex);
Assert.AreEqual("Attachment 2", bookmarks[2].Bookmarks[0].Bookmarks[1].Title);
Assert.AreEqual(11, bookmarks[2].Bookmarks[0].Bookmarks[1].PageIndex);
Я застрял на рекурсивная часть (я думаю ...), я могу l oop через каждый FlatBookmark
и разделить путь (string[] parts = bookmark.Split('/')
), а затем для каждой части я хочу как-то оглянуться назад, если у нее есть родители, или должен иметь каких-либо родителей, а если нет: создать их ...
Может кто-то, возможно, указать мне правильное направление, как создать метод, подобный этому? на количество вложенных элементов ...
--- ОБНОВЛЕНИЕ ---
- Обновлен список flatBookmark с другим разделителем
- Убрано требование «Путь» в выходной закладке (достаточно заголовка)
- Дополнительное требование: порядок плоских закладок должен быть сохранились; несколько закладок на одном уровне с одним и тем же названием должны объединяться только в том случае, если между ними нет другого названия
--- РЕШЕНИЕ ---
Вот решение, которое я придумал:
public List<Bookmark> CreateNestedBookmarks(List<FlatBookmark> flatBookmarks, string separator = "/")
{
// Create a 'root' bookmark list (happens for every nested level, with recursion)
var bookmarks = new List<Bookmark>();
foreach (var flatBookmark in flatBookmarks)
{
if(!string.IsNullOrWhiteSpace(flatBookmark.Path))
{
// Only use the title
string title = flatBookmark.Path.Split(new string[] { separator }, StringSplitOptions.None)[0];
bool exists = bookmarks.LastOrDefault()?.Title == title;
// Add the bookmark to the list if it's not already added before and not empty/null
if (!exists && !string.IsNullOrWhiteSpace(title))
{
bookmarks.Add(new Bookmark()
{
Title = title,
PageIndex = flatBookmark.PageIndex
});
}
}
}
// Fetch the nested bookmarks for each 'root'-bookmark
foreach(var bookmark in bookmarks)
{
bool found = false;
List<FlatBookmark> childBookmarks = new List<FlatBookmark>();
foreach (var flatBookmark in flatBookmarks)
{
if(!string.IsNullOrWhiteSpace(flatBookmark.Path))
{
string[] splittedPath = flatBookmark.Path.Split(new string[] { separator }, StringSplitOptions.None);
string title = splittedPath[0];
if (title == bookmark.Title)
{
// Strip the first part of the path
flatBookmark.Path = string.Join(separator, splittedPath.Skip(1).Take(splittedPath.Length - 1));
childBookmarks.Add(flatBookmark);
found = true;
}
else
{
// Stop iteration when a new title is found, with this way it keeps the input order intact
// Multiple bookmarks on the same level with the same title should only be combined when there is no different title in between
// Cat1
// Cat2
// Cat1
if (found)
{
break;
}
}
}
}
// Create nested bookmarks (if they exist)
if(childBookmarks.Count > 0)
{
bookmark.Bookmarks = CreateNestedBookmarks(childBookmarks, separator);
}
}
return bookmarks;
}