c# - 使用 linq 查找树视图子节点的路径。(完全展平树视图)
问题描述
我有一个树视图:
A0 -----
A1 -----
A2
B0 ----
B1 ----
C0 ----
C1 ----
主分支 A0、A1、A2 的类型相同。B0、B1 属于同一类型。C0、C1 属于同一类型。A型、B型和C型是不同的类型。我需要从每个顶部节点向下通过其所有子分支到其终端子节点的路径。也就是说(假设每个级别都可以表示为一个字符串),输出需要是:
- A0
- A1
- A2 -- B0
- A2 -- B1 -- C0
- A2 -- B1 -- C1
每个列表都是一条路径。
这个问题让我觉得它类似于数据库表上的左连接。如何才能做到这一点?(林克??)。
谢谢你的帮助。
编辑#1:这是每个分支的完整定义。希望能帮助到你。
显示更多信息,每个“A”对象都是通过以下方式创建的:
view_consulting[] v = MyNetwork.Medical.Client.GetConsultingStaff();
var s = v.GroupBy(p => p.speciality_name).Select(q => new SpecialityTreeItem(q));
那就是“A”是SpecialityTreeItem。
public class SpecialityTreeItem : SimpleViewModelBase
{
#region [Fields]
private string _speciality;
#endregion
#region [Constructors]
public SpecialityTreeItem() { }
public SpecialityTreeItem(string speciality_name )
{
Speciality = speciality_name;
var _offices = MyNetwork.Medical.Client.GetOffices(speciality_name);
Offices = new ObservableCollection<OfficeTreeItem>(
_offices.Select(q => new OfficeTreeItem(q.office_name, Speciality) { City = q.city, Phone = q.phone, Fax = q.fax }));
}
public SpecialityTreeItem(IGrouping<string, view_consulting> specialityGroup)
{
Speciality = specialityGroup.Key;
// Create a subgroup for office under each SpecialityTreeItem for each office name.
var _offices = specialityGroup.Where(q => !string.IsNullOrEmpty(q.office_name))
.GroupBy(q => q.office_name)
.Select(y => new OfficeTreeItem(y)).ToList();
Offices = new ObservableCollection<OfficeTreeItem>(_offices);
}
// Constructor to build a tree with single branches per level. (i.e., only one office per speciality, or one doctor per office).
public SpecialityTreeItem(SpecialityTreeItem sti, OfficeTreeItem oti, DoctorTreeItem dti)
{
Speciality = sti.Speciality;
Offices = new ObservableCollection<OfficeTreeItem>();
if (oti != null)
{
var m = new OfficeTreeItem[]{ new OfficeTreeItem(oti,dti)};
Offices = new ObservableCollection<OfficeTreeItem>(m);
}
}
#endregion
#region [Properties]
public string Speciality
{
get { return _speciality; }
set { if (_speciality == value) return; _speciality = value; RaisePropertyChanged(); }
}
public ObservableCollection<OfficeTreeItem> Offices { get; set; }
#endregion
}
第二级“B”是办公室:
public class OfficeTreeItem : SimpleViewModelBase
{
#region [Fields]
private string _speciality;
private string _officeName;
private string _city;
private string _phone;
private string _fax;
#endregion
#region [Constructors]
public OfficeTreeItem() { }
public OfficeTreeItem(IGrouping<string, view_consulting> officeGroup)
{
OfficeName = officeGroup.Key;
Speciality = officeGroup.First().speciality_name;
var _doctors = officeGroup.Where(q => !string.IsNullOrEmpty(q.lastname))
.GroupBy(q => new { q.lastname, q.firstname, q.speciality_name })
.Select(y => new DoctorTreeItem() {
FirstName = y.Key.firstname, LastName =y.Key.lastname, Speciality = y.Key.speciality_name
});
Doctors = new ObservableCollection<DoctorTreeItem>(_doctors);
}
public OfficeTreeItem(string Office_Name, string speciality_name)
{
OfficeName = Office_Name;
Speciality = speciality_name;
view_consultant[] _doctor_office = MyNetwork.Medical.Client.GetConsultants(OfficeName, Speciality);
Doctors = new ObservableCollection<DoctorTreeItem>(
_doctor_office.Select(q => new DoctorTreeItem() { LastName = q.lastname, FirstName = q.firstname, Speciality = q.specialty_name}
));
}
public OfficeTreeItem(OfficeTreeItem oti, DoctorTreeItem dti)
{
OfficeName = oti.OfficeName;
City = oti.City;
Phone = oti.Phone;
Fax = oti.Fax;
Doctors = new ObservableCollection<DoctorTreeItem>();
if (dti != null)
{
var m = new DoctorTreeItem[]{dti};
Doctors = new ObservableCollection<DoctorTreeItem>( m );
}
}
#endregion
#region [Properties]
public ObservableCollection<DoctorTreeItem> Doctors { get; set; }
public string Speciality
{
get { return _speciality; }
set { if (_speciality == value) return; _speciality = value; RaisePropertyChanged(); }
}
public string OfficeName
{
get { return _officeName; }
set { if (_officeName == value) return; _officeName = value; RaisePropertyChanged(); }
}
public string City
{
get { return _city; }
set { if (_city == value) return; _city = value; RaisePropertyChanged(); }
}
public string Phone
{
get { return _phone; }
set { if (_phone == value) return; _phone = value; RaisePropertyChanged(); }
}
public string Fax
{
get { return _fax; }
set { if (_fax == value) return; _fax = value; RaisePropertyChanged(); }
}
#endregion
}
}
最后的“C”级是医生:
public class DoctorTreeItem : SimpleViewModelBase
{
#region [Fields]
private string _lastName;
private string _firstName;
private string _speciality;
#endregion
#region [Constructor]
public DoctorTreeItem()
{
}
public DoctorTreeItem(DoctorTreeItem dti)
{
LastName = dti.LastName;
FirstName = dti.FirstName;
Speciality = dti.Speciality;
}
#endregion
#region [Properties]
public string LastName
{
get { return _lastName; }
set { if (_lastName == value) return; _lastName = value; RaisePropertyChanged(); }
}
public string FirstName
{
get { return _firstName; }
set { if (_firstName == value) return; _firstName = value; RaisePropertyChanged(); }
}
public string Speciality
{
get { return _speciality; }
set { if (_speciality == value) return; _speciality = value; RaisePropertyChanged(); }
}
#endregion
}
}
解决方案
看起来你需要一个递归函数,而不是 linq ......
如果你有一个带有节点和任意层次结构的通用树视图控件,你可以有一个类似于下面的函数:
public string GetPaths(int pathCounter, TreeNodeCollection nodes)
{
StringBuilder sb = new StringBuilder();
foreach(TreeNode node in nodes)
{
if (node.Nodes.Count == 0)
sb.Append($"{pathCounter++} {node.FullPath}\n");
else
sb.Append(GetPaths(pathCounter, node.Nodes));
}
return sb.ToString();
}
但是,您在层次结构中有三个特定且不同的类。所以,对于这个特定的例子,我会写这样的东西:
public string GetPaths(IEnumerable<SpecialityTreeItem> stil)
{
int pathCounter;
StringBuilder sb = new StringBuilder();
foreach(var sti in stil)
{
if (sti.Offices.Count == 0)
sb.Append($"{pathCounter++} {sti.Speciality}\n");
else
{
foreach (var oti in sti.Offices)
{
if (oti.Doctors.Count == 0)
sb.Append($"{pathCounter++} {sti.Speciality} -- {oti.OfficeName}\n");
else
{
foreach (var dti in oti.Doctors)
{
sb.Append($"{pathCounter++} {sti.Speciality} -- {oti.OfficeName} -- {dti.LastName}, {dti.FirstName}\n");
}
}
}
}
}
return sb.ToString();
}
这有点难看。问题是层次结构中的每个类都有不同的属性来到达层次结构中的下一个级别,因此不能使用递归函数。如果您可以使用通用名称而不是Offices
and Doctors
,那可能会有所帮助,但您DoctorTreeItem
不支持拥有从属项。
至少,我会覆盖ToString()
所有类中的方法,这样上面概述的有点丑陋的方法就会变得更简洁一些,如下所示:
public string GetPaths(IEnumerable<SpecialityTreeItem> stil)
{
int pathCounter;
StringBuilder sb = new StringBuilder();
foreach(var sti in stil)
{
if (sti.Offices.Count == 0)
sb.Append($"{pathCounter++} {sti}\n");
else
{
foreach (var oti in sti.Offices)
{
if (oti.Doctors.Count == 0)
sb.Append($"{pathCounter++} {sti} -- {oti}\n");
else
{
foreach (var dti in oti.Doctors)
{
sb.Append($"{pathCounter++} {sti} -- {oti} -- {dti}\n");
}
}
}
}
}
return sb.ToString();
}
希望这能回答你的问题。
推荐阅读
- c# - 保存时出错:指定的演员表无效
- html - 将 CSS 提取到 HTML 标签中
- mysql - MySQL数据库Nodejs,初始表查询
- gimp - script-fu 控制台上的 GIMP 复制层
- python - 单行for循环
- sparql - SPARQL 的 FILTER() 没有按预期工作
- r - 匹配功能无法找到现有匹配
- c# - 使用 x509.ParseCertificateRequest 解组失败但 openssl 可以吗?
- ios - 将菜单编程到 TableView 单元格中
- android - 了解 Tab 活动的代码。PagerAdapter 和 Placeholder 是什么关系