我有一个包含行星及其卫星作为儿童系列的集合 它是一个集合,但它确实代表了树状结构。为简单起见,我只显示了2个树级别,但每个行星或月球都可以进一步拥有化学元素的集合,所以为了简单起见,我只使用2级树。
Mercury
Venus
Mars
- Deimos
- Phobos
Jupiter
- Europa
- Ganymede
- Io
我知道如何将此集合转换为列表,我只需使用
var myList = myCollection.Values.ToList();
我想在此列表中搜索名称中包含“m”的每个项目。如果父母的名字中没有“m”,但是它的任何子卫星都有,我想包括那个孩子(月亮)和它的父母(星球)。在木星的情况下,我会在我的列表中包括木星和木卫三。
我对“m”的搜索将返回以下列表
{Mercury,Mars,Deimos,Jupiter,Ganymede}
我更喜欢使用lambda,但不需要这样做
更新:结构
BodyNode
-ID [Guid]
-Name [string]
-IsChild [bool]
-Parent [BodyNode]
-Children[BodyList ObservableCollection of BodyNode]
BodyTreeNode : BodyNode
-Expanded [bool]
-Selected [bool]
-Enabled [bool]
答案 0 :(得分:2)
如果您确定您的数据是树状结构,那么您可以执行类似的操作(不检查周期):
bool GetNodes(Tree root, List<Tree> result, Func<Tree, bool> f) {
bool add = f(root);
foreach (var child in root.Children) {
add ||= GetNodes(child, result, f);
}
if (add)
result.Add(root);
return add;
}
f
是让您知道是否添加树的功能。例如。 (t)=>t.Name.Contains("m")
。
编辑:假设所有对象派生自Base,Base具有属性public List<Base> GetChildren{get;}
,则上述逻辑可以实现为:
bool GetNodes(Base b, List<Base> result, Func<Base, bool> f) {
bool add = f(b);
foreach (var child in b.GetChildren) {
add ||= GetNodes(child);
}
if (add) result.Add(b);
return add;
}
然后您将其用作:
var r = new List<Base>();
myList.Foreach(o => GetNodes(o, r, (b) => b.Name.Contains("m"));
答案 1 :(得分:2)
假设您有一系列集合的所有元素。然后,您可以在该序列上使用Where
或任何其他序列运算符。因此,最好的办法是建立这个序列:
static class Extensions {
public static IEnumerable<Nodes> Flatten(this IEnumerable<Node> nodes)
{
foreach(var node in nodes)
{
yield return node;
foreach (var child in node.Children.Flatten())
yield return child;
}
}
}
容易腻。现在你可以简单地说:
var results = from node in myCollection.Values.Flatten()
where node.Name.ToLower().Contains("m")
select node;
这是名称中包含m的每个元素的列表:
或使用lambda
var results = myCollection.Values.Flatten()
.Where(node => ... and so on ... );
现在,您想要的是名称中包含m或任何子项的每个元素的列表。所以写下来;我们拥有所需的所有工具!
var results = from node in myCollection.Values.Flatten()
where node.Name.Contains("m") || node.Children.Flatten().Any(node.Name.Contains("m"))
select node;
现在,这效率很低 - 你能明白为什么吗? - 但它可以解决问题。现在你有一些可以分析的工作,并且如果你真的需要的话,可以提高效率。
答案 2 :(得分:1)
var MRecords = myList.Where(x=>x.toUpper().Contains("M"));
var result = new HashSet<"yourClass">(); //i didn`t use hashset before but this as stated in [here](http://stackoverflow.com/questions/6391738/what-is-the-difference-between-hashsett-and-listt) eliminates duplicates
foreach(var record in MRecords)
{
result.Add(record);
var ParentLooper = record;
while(ParentLooper.parent!=null) //i suppose roots have the parent as null
{
result.add(ParentLooper.parent);
ParentLooper = ParentLooper.parent;
}
}
return result;
答案 3 :(得分:0)
就个人而言,我建议在处理这样的树实体时使用包含objects
个自定义类的IEnumerables
。看起来你的Planet
和你的月亮都具有相同的属性,所以它们基本上都属于Planet
类。鉴于我们只处理名称,我创建了一个私有模型类,如下所示:
private class Planet
{
public string Name { get; set; } = string.Empty;
public IEnumerable<Planet> Children { get; set; } = new List<Planet>();
}
这使得Lambdas的使用更简单一些。我没有测试这个,但是如果它不正确它接近它并且应该让你到那里:
IEnumerable<Planet> myCollection = new List<Planet>();
myCollection = LetThereBeLight(true); //method to populate the list
var myList = myCollection.ToList().Where(t => t.Name.ToUpper().Contains("M"));
myList.ToList().AddRange
(
myCollection.SelectMany(t => t.Children.Where
(
v => v.Name.ToUpper().Contains("M")).Distinct()
)
);
你可以把它全部变成一个Lambda,但是如果不把它全部压成一行就很难阅读。这将为您提供一个IEnumerable<Planet>
如果您想表示卫星的差异,例如在名称string
中添加“ - ”,则可以遍历每个Planet's
{{1收集。您可能希望确保仅使用Children
,因为此处完全可以复制。