使用1 LINQ语句从分层数据填充树
我已经设置了这个编程练习使用1 LINQ语句从分层数据填充树,linq,Linq,我已经设置了这个编程练习 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication2 { class DataObject { public int ID { get; set; } public int ParentID { get; set; } public stri
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class DataObject {
public int ID { get; set; }
public int ParentID { get; set; }
public string Data { get; set; }
public DataObject(int id, int pid, string data) { this.ID = id; this.ParentID = pid; this.Data = data; }
}
class TreeNode {
public DataObject Data {get;set;}
public List<DataObject> Children { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<DataObject> data = new List<DataObject>();
data.Add(new DataObject(1, 0, "Item 1"));
data.Add(new DataObject(2, 0, "Item 2"));
data.Add(new DataObject(21, 2, "Item 2.1"));
data.Add(new DataObject(22, 2, "Item 2.2"));
data.Add(new DataObject(221, 22, "Item 2.2.1"));
data.Add(new DataObject(3, 0, "Item 3"));
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
命名空间控制台应用程序2
{
类数据对象{
公共int ID{get;set;}
public int ParentID{get;set;}
公共字符串数据{get;set;}
公共数据对象(int-id,int-pid,字符串数据){this.id=id;this.ParentID=pid;this.data=data;}
}
三烯类{
公共数据对象数据{get;set;}
公共列表子项{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
列表数据=新列表();
添加(新的数据对象(1,0,“第1项”);
添加(新的数据对象(2,0,“第2项”);
添加(新数据对象(21,2,“第2.1项”);
添加数据(新数据对象(22,2,“第2.2项”);
添加数据(新数据对象(221、22,“第2.2.1项”);
添加(新的数据对象(3,0,“第3项”);
}
}
}
所需的输出是3个三节点的列表,具有项目1、2和3。项2将有一个包含2个数据对象的列表作为其子成员,依此类推
我一直在尝试使用LINQ中的一条语句来填充这棵树(或者更确切地说是一个林)。一个简单的GROUPBY给了我所需的数据,但挑战是如何在TreeNode对象中组织它
有人能给你一个提示或者一个不可能的结果吗?如果你只想要两个层次的树,它非常简单:
var roots = from root in data
where !data.Any(d => root.ParentID == d.ID)
select new TreeNode
{
Data = root,
Children = data.Where(d => d.ParentID == root.ID).ToList(),
}
如果只需要两层树,则非常简单:
var roots = from root in data
where !data.Any(d => root.ParentID == d.ID)
select new TreeNode
{
Data = root,
Children = data.Where(d => d.ParentID == root.ID).ToList(),
}
LINQ在递归数据结构方面做得不是特别好,但是您可以接近它 由于您可能需要任意深度的树,我将更改您对子级的定义,并添加一个构造函数和一个助手方法,该方法将执行使树构建LINQ语句成为可能所需的递归
class TreeNode
{
public DataObject Data { get; set; }
public List<TreeNode> Children { get; private set; }
public TreeNode(DataObject data)
{
Data = data;
Children = new List<TreeNode>();
}
//name chosen to match XElement method. I would name this
//SelfAndDescendants or change the behavior to match the name.
public IEnumerable<TreeNode> DescendantsAndSelf()
{
return (new TreeNode[] { this }).Concat(from c in this.Children
from sc in c.DescendantsAndSelf()
select sc);
}
}
类树节点
{
公共数据对象数据{get;set;}
公共列表子项{get;private set;}
公共树节点(数据对象数据)
{
数据=数据;
Children=新列表();
}
//选择与XElement方法匹配的名称。我将为其命名
//selfandsubjects或更改行为以匹配名称。
公共IEnumerable后代和self()
{
return(newtreenode[]{this}).Concat(在this.Children中来自c
c.后代和自我()中的sc
选择sc);
}
}
现在我们可以定义以下方法:
public static TreeNode BuildTree(IEnumerable<DataObject> items, int rootId)
{
var root = new TreeNode(new DataObject(rootId, int.MinValue, "Root"));
return (from i in items
let n = new TreeNode(i)
group n by n.Data.ParentID into nodeGroup
orderby nodeGroup.Key ascending
select nodeGroup)
.Aggregate(root, (parent, childGroup) =>
{
parent.DescendantsAndSelf()
.First(n => n.Data.ID == childGroup.Key)
.Children.AddRange(childGroup);
return parent;
});
}
publicstatictreeNodebuildtree(IEnumerable项,int rootId)
{
var root=newtreenode(新数据对象(rootId,int.MinValue,“root”);
返回(从项目中的i开始)
设n=新树节点(i)
按n.Data.ParentID将n分组到节点组中
orderby节点组。键升序
选择节点组)
.聚合(根,(父,子组)=>
{
parent.genderantsandself()
.First(n=>n.Data.ID==childGroup.Key)
.childreng.AddRange(childGroup);
返回父母;
});
}
这种方法会做出一些假设,但应该会让你走上正确的方向
- 传入的元素都不是树的根节点,将创建一个根节点作为返回值
- 父节点的id始终低于节点的id(这允许调用
+OrderBy
将项目推送到树中)Aggregate
- 传递给该方法的序列中的每个项都将属于根或其他项之一(否则,
调用将引发异常).First()
class TreeNode
{
public DataObject Data { get; set; }
public List<TreeNode> Children { get; private set; }
public TreeNode(DataObject data)
{
Data = data;
Children = new List<TreeNode>();
}
//name chosen to match XElement method. I would name this
//SelfAndDescendants or change the behavior to match the name.
public IEnumerable<TreeNode> DescendantsAndSelf()
{
return (new TreeNode[] { this }).Concat(from c in this.Children
from sc in c.DescendantsAndSelf()
select sc);
}
}
类树节点
{
公共数据对象数据{get;set;}
公共列表子项{get;private set;}
公共树节点(数据对象数据)
{
数据=数据;
Children=新列表();
}
//选择与XElement方法匹配的名称。我将为其命名
//selfandsubjects或更改行为以匹配名称。
公共IEnumerable后代和self()
{
return(newtreenode[]{this}).Concat(在this.Children中来自c
c.后代和自我()中的sc
选择sc);
}
}
现在我们可以定义以下方法:
public static TreeNode BuildTree(IEnumerable<DataObject> items, int rootId)
{
var root = new TreeNode(new DataObject(rootId, int.MinValue, "Root"));
return (from i in items
let n = new TreeNode(i)
group n by n.Data.ParentID into nodeGroup
orderby nodeGroup.Key ascending
select nodeGroup)
.Aggregate(root, (parent, childGroup) =>
{
parent.DescendantsAndSelf()
.First(n => n.Data.ID == childGroup.Key)
.Children.AddRange(childGroup);
return parent;
});
}
publicstatictreeNodebuildtree(IEnumerable项,int rootId)
{
var root=newtreenode(新数据对象(rootId,int.MinValue,“root”);
返回(从项目中的i开始)
设n=新树节点(i)
按n.Data.ParentID将n分组到节点组中
orderby节点组。键升序
选择节点组)
.聚合(根,(父,子组)=>
{
parent.genderantsandself()
.First(n=>n.Data.ID==childGroup.Key)
.childreng.AddRange(childGroup);
返回父母;
});
}
这种方法会做出一些假设,但应该会让你走上正确的方向
- 传入的元素都不是树的根节点,将创建一个根节点作为返回值
- 父节点的id始终低于节点的id(这允许调用
+OrderBy
将项目推送到树中)Aggregate
- 传递给该方法的序列中的每个项都将属于根或其他项之一(否则,
调用将引发异常).First()