我有DataTable
个对象,它拥有一些“树数据结构”。数据不存储在任何数据库中,我只使用DataTable来操作没有SQL服务器的数据。
我的数据看起来像这样(缩进只是为了更好地阅读):
DataTable dtCategories = GetCategoriesAsDataTable();
id name parentId
int string int
----------------------
1 One 0
2 OneA 1
3 OneB 1
4 Two 0
5 TwoA 4
6 TwoB 4
7 TwoAA 5
8 TwoAB 5
到目前为止 - 我正在考虑选择“where parentId = 0”的第一级并将其放在单独的DataTable中,如下所示:
DataTable dtFirstLevel = dtCategories.Select("[parentId] = 0");
// and after this - create DataTable for second level
// but I don't know how can I use "IN" clause here
DataTable dtSecondLevel = dtCategories.Select(?????????);
答案 0 :(得分:3)
也许这会有所帮助:
var rows = table.AsEnumerable();
var parents = rows.Where(r => !r.Field<int?>("parentId").HasValue);
var children = rows.Where(r => r.Field<int?>("parentId").HasValue);
var secondLevel = from parent in parents
join child in children
on parent.Field<int>("id") equals child.Field<int?>("parentId").Value
select child;
var both = parents.Concat(secondLevel).CopyToDataTable();
请注意,我为父母使用Nullable<int>
而不是0,因为它更易读,更不容易出错。以下是您的示例数据:
var table = new DataTable();
table.Columns.Add("id", typeof(int));
table.Columns.Add("name", typeof(string));
table.Columns.Add("parentId", typeof(int));
table.Rows.Add(1, "One", (int?)null);
table.Rows.Add(2, "OneA", 1);
table.Rows.Add(3, "OneB", 1);
table.Rows.Add(4, "Two", (int?)null);
table.Rows.Add(5, "TwoA", 4);
table.Rows.Add(6, "TwoB", 4);
table.Rows.Add(7, "TwoAA", 5);
table.Rows.Add(8, "TwoAB", 5);
结果:
1 One
4 Two
2 OneA 1
3 OneB 1
5 TwoA 4
6 TwoB 4
由于您希望使用0
代替int?
:
var parents = rows.Where(r => r.Field<int>("parentId") == 0);
var children = rows.Where(r => r.Field<int>("parentId") != 0);
var secondLevel = from parent in parents
join child in children
on parent.Field<int>("id") equals child.Field<int>("parentId")
select child;
答案 1 :(得分:2)
我认为此功能可以帮助您确定每个条目的树级别,以便您可以在选择中使用它:
public int level(DataTable dt, DataRow row)
{
int parentid = int.Parse(row[2].ToString());
if (parentid == 0)
return 1;
else
return 1 + level(dt, GetDataRow(dt,parentid ));
}
public DataRow GetDataRow(DataTable dt, int id)
{
foreach (DataRow r in dt.Rows)
{
if (int.Parse(r[0].ToString()) == id) return r;
}
return null;
}
答案 2 :(得分:1)
您有几个问题可供选择。正如@Ali所提出的,你可以使用这样的递归:
public int level(DataTable dt, DataRow row)
{
int parentid = int.Parse(row[2].ToString());
if (parentid == 0)
return 1;
else
return 1 + level(dt, GetDataRow(dt,parentid ));
}
public DataRow GetDataRow(DataTable dt, int id)
{
foreach (DataRow r in dt.Rows)
{
if (int.Parse(r[0].ToString()) == id) return r;
}
return null;
}
但问题是你最终会迭代每个元素然后在每次迭代时使用递归。如果您的列与它们在树中的级别之间绝对没有数据关系,那么除了parentId之外,这是您唯一的解决方案。
另一方面,如果你有一个关系,你有名字[树的级别],如名[A]是树级别1,名称[AB]是树级别2与右边的节点,然后迭代通过每个人都喜欢:
foreach (DataRow r in dt.Rows)
{
//Pull out the element
//Check the element's level
//Add it to the result set if level <= 2
}
我个人更喜欢通过实际构建树结构或使用SQL WHERE子句来解决问题,但很难证明它的时间是合理的。根据您从何处获取此数据,您还可以添加一个额外的列,该列可以告诉您节点所在的级别,具体取决于插入的位置。如果它有祖父母(即两个父节点),则不将其包含在结果集中。
答案 3 :(得分:1)
DataTable level1 = (from t in dtCategories.AsEnumerable()
where t.Field<int>("parentId") == 0
select t).CopyToDataTable();
DataTable level2 =(from t1 in dtCategories.AsEnumerable()
join t2 in dtCategories.AsEnumerable()
on t1.Field<int>("id") equals t2.Field<int>("parentId")
where t1.Field<int>("parentId") == 0
select t2).CopyToDataTable();
答案 4 :(得分:1)
另一种方法,这将为您提供一个新对象,其中包含级别和行项本身。这将适用于n个级别......
var nodes = table.AsEnumerable();
//var nodes = new List<TreeNode>();
var parentId = 0;
var countLevel = 0;
var allNods = new List<dynamic>();
while (nodes.Any(p => p.Field<int>("parentId") == parentId))// && countLevel < 2)
// countlevel< 2 only to give you the first 2 levels only...
{
var nodesWithLevel = nodes.Where(p => p.Field<int>("parentId") == parentId)
.Select(p => new { Level = parentId, Node = p });
allNods = allNods.Concat<dynamic>(nodesWithLevel).ToList();
parentId++;
countLevel++;
}
代码当前期望根节点具有parentId = 0.也可以将原因更改为null ...