我有一个List,其中列表的内容用空格缩进。
有两列(控件类型和ID),列有固定长度。
缩进表示元素之间的父子关系。
举个例子:
Modal 000
Child1 100
Child1 110
Child2 200
Child1 210
Child2 220
Child1 221
Child3 300
要保存信息,我有以下简单类:
class CustomDefinition
{
public List<CustomControl> Children { get; set; }
}
class CustomControl
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public List<CustomControl> Children { get; set; }
}
计划是拥有一个CustomDefinition
类的实例,它将包含根元素,而其他元素(根元素的子元素)将位于根本身的子集合中。根元素的ParentId
为000
,而子元素应该接收其父元素的id。
由于孩子们自己有孩子,他们将被安置在自己的孩子们的收藏中。
我在这里找到了一个扩展方法,它为我提供了第一个非空格字符的索引,但我不知道应该如何继续:
private static void _TestParse(List<string> items)
{
CustomDefinition definition = new CustomDefinition() {
Children = new List<CustomControl>()
};
int currentControlId = 0;
for (int i = 0; i < items.Count; i++)
{
// the current element
var control = items[i];
// the aforementioned extension method
var controlIndex = control.IndexOf<char>(c => !char.IsWhiteSpace(c));
// fixed length for the first column
var controlName = control.Substring(0, 20).Trim();
// fixed length for the second column
var controlId = control.Substring(20, 3);
int.TryParse(controlId, out currentControlId);
definitions.Children.Add(new CustomControl() {
Id = currentControlId,
Name = controlName
});
// i need to find if the current element has children
// (in the example above the root element has 3 children)
// then add these children to the current element
var currentElement = definitions.Children
.Find(x => x.Id == currentControlId);
// then do this for all children and their children?!
}
}
我希望这一切都有意义,我感谢任何帮助。
答案 0 :(得分:1)
如果我从这些数据开始:
var data = new []
{
"Modal 000",
" Child1 100",
" Child1 110",
" Child2 200",
" Child1 210",
" Child2 220",
" Child1 221",
" Child3 300",
};
然后我可以创建一个这样的原始项目列表:
var items =
from x in data
let idText = x.Substring(20)
let indentedText = x
.Substring(0, x.Length - idText.Length)
.TrimEnd()
let name = indentedText.Trim()
let indent = indentedText.Length - name.Length
select new
{
id = int.Parse(idText),
name,
indent,
};
这让我:
现在我可以使用此代码将其转换为所需的对象模型:
var itemDictionary = new Dictionary<int, CustomControl>();
foreach (var item in items)
{
itemDictionary[item.indent] = new CustomControl()
{
Id = item.id,
Name = item.name,
Children = new List<CustomControl>(),
};
if (item.indent != 0)
{
itemDictionary[item.indent].ParentId =
itemDictionary[item.indent - 1].Id;
itemDictionary[item.indent - 1]
.Children.Add(itemDictionary[item.indent]);
}
}
然后我得到了这个输出:
答案 1 :(得分:0)
在Main
方法中,我创建了示例数据,初始化了类,并对数据进行了解析。有一个递归方法负责搜索当前节点的父节点。我不得不修改你的CustomControl
课程。在示例中查看ID的结构,将它们保存为字符串会更加明显(如果您喜欢它们,则将其恢复为int - 只需要稍微修改我的代码)。我还在CustomControl
添加了一个属性。它负责记住项目的缩进级别。
代码中的更多解释作为注释。代码下方:
static void Main(string[] args)
{
// Create sample data
List<string> items = new List<string>();
items.Add("Modal 000");
items.Add(" Child1 100");
items.Add(" Child1 110");
items.Add(" Child2 200");
items.Add(" Child1 210");
items.Add(" Child2 220");
items.Add(" Child1 221");
items.Add(" Child3 300");
// Initialize the collection object
CustomDefinition customDefinition = new CustomDefinition();
customDefinition.Children = new List<CustomControl>();
// Keep reference to the previous item in the list
CustomControl prevItem = null;
for (int i = 0; i < items.Count; ++i)
{
// Find indentation level
int currentIndent = items[i].TakeWhile(c => char.IsWhiteSpace(c)).Count();
string[] itemIdPair = items[i].Trim().Split(' ').ToArray();
// Create CustomControl from data
CustomControl currentItem = new CustomControl();
// Before split the string is trimmed so name is always first item and ID - last one
currentItem.Id = (itemIdPair[itemIdPair.Length -1]);
currentItem.Name = itemIdPair[0];
currentItem.IndentLevel = currentIndent;
// Add CustomControl reference to the collection
customDefinition.Children.Add(currentItem);
// First item - main node
if (prevItem == null)
{
prevItem = currentItem;
}
// Siblings - get the same parent
else if (currentItem.IndentLevel == prevItem.IndentLevel)
{
CustomControl parent = customDefinition.Children.Where(item => item.Id == prevItem.ParentId).FirstOrDefault();
currentItem.ParentId = parent.Id;
if (parent.Children == null)
parent.Children = new List<CustomControl>();
parent.Children.Add(currentItem);
}
// Child
else if (currentItem.IndentLevel > prevItem.IndentLevel)
{
currentItem.ParentId = prevItem.Id;
if (prevItem.Children == null)
prevItem.Children = new List<CustomControl>();
prevItem.Children.Add(currentItem);
}
// Item's level is higher than the previous item - find how far does it go and
// get the parent for the item
else
{
// parent's indentation will be one level higher than the current one (currentIndent - 1)
// try to find it recursively
// Use the previous node in the list of nodes as the starting point
CustomControl parent = GoBackToLevel(customDefinition.Children, prevItem, currentIndent - 1);
currentItem.ParentId = parent.Id;
if (parent.Children == null)
parent.Children = new List<CustomControl>();
parent.Children.Add(currentItem);
}
// Replace the reference to the previous item.
prevItem = currentItem;
}
}
private static CustomControl GoBackToLevel(List<CustomControl> items, CustomControl start, int parentLevel)
{
// Find a parent of the starting CustomControl using the control's ID.
CustomControl parent = items.Where(item => item.Id == start.ParentId).FirstOrDefault();
// Does the parent has the expected indentation level? Yes - get the parent
// else use the parent as the starting point for search instead.
if (parent.IndentLevel == parentLevel)
return parent;
else
return GoBackToLevel(items, parent, parentLevel);
}
修改后的CustomControl
:
class CustomControl
{
public string Id { get; set; }
public string ParentId { get; set; }
public string Name { get; set; }
public List<CustomControl> Children { get; set; }
public int IndentLevel { get; set; }
}