我有相当多的foo { int id, int parentid, string name}
.
我正在寻找一个对象列表,foo
其中对象的名称为“bar3”,并且是名为“bar2”的对象的子对象,该对象是 ID 为 的对象的子对象1
。
我应该使用什么样的集合(我一直在使用查找和字典,但并没有取得很大的成功),我应该如何编写它以从中产生有效的功能?大约有 30K 个foo
对象,我的方法快窒息而死。
谢谢!
我有相当多的foo { int id, int parentid, string name}
.
我正在寻找一个对象列表,foo
其中对象的名称为“bar3”,并且是名为“bar2”的对象的子对象,该对象是 ID 为 的对象的子对象1
。
我应该使用什么样的集合(我一直在使用查找和字典,但并没有取得很大的成功),我应该如何编写它以从中产生有效的功能?大约有 30K 个foo
对象,我的方法快窒息而死。
谢谢!
如果我真的必须坚持这个布局foo
,我真的必须尽可能快地进行查找(我不关心内存大小,并且会重复重用相同的对象,所以设置一组的成本内存中的大型结构是值得的),那么我会这样做:
var byNameAndParentLookup = fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name)); //will reuse this repeatedly
var results = byNameAndParentLookup[Tuple.Create(1, "bar2")].SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
也就是说,如果我要将树数据存储在内存中,我更愿意创建一个树结构,其中每个foo
都有一个children
集合(可能是一个以名称为键的字典)。
编辑:解释一下。
fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name))
遍历fooSource
(无论我们的foo
对象来自何处)中的所有项目,并为每个项目创建一个元组 theparentid
和name
. 这用作查找的键,因此对于每个 parentid-name 组合,我们可以使用该组合检索 0 个或多个 foo 对象。(这将使用默认字符串比较,如果您想要其他内容(例如不区分大小写),请创建一个IEqualityComparer<Tuple<int, string>>
执行您想要的比较并使用的实现.ToLookup(f => Tuple.Create(f.parentid, f.name), new MyTupleComparer())
)。
第二行可以分解为:
var partWayResults = byNameAndParentLookup[Tuple.Create(1, "bar2")];
var results = partWayResults.SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
第一行只是简单地对我们的查找进行搜索,因此它返回了父 ID 为 1 且名称为“bar2”的那些 foo 对象的枚举。
SelectMany
获取枚举或可查询的每个项目,并计算返回枚举的表达式,然后将其展平为单个枚举。
换句话说,它的工作原理有点像这样:
public static SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> func)
{
foreach(TSource item in source)
foreach(TResult producedItem in func(item))
yield return producedItem;
}
在我们的例子中,通过的表达式获取在第一次查找中找到的元素的 id,然后查找任何以它为 parentid 并且名称为“bar2”的元素。
因此,对于每个 parentid 为 1 且名称为 bar2 的项目,我们找到每个项目的第一个项目的 id 作为其 parentid 和名称 bar3。这就是想要的。
看看这个:QuickGraph 我从来没有真正使用过它,但它似乎有据可查。或者,您可以尝试C5 通用集合库
我从这个胎面得到这个
我可以建议您先按 parentId 对所有项目进行分组,然后对其应用条件。首先,您需要找到带有 bar1 元素的组,而不是选择其所有子元素并尝试查找名称为 bar 2 的元素...
我可以建议这样的解决方案,它不是最好的,但它有效(thirdLevelElements 将包含所需的元素)。我用 foreachs 说清楚了,这个逻辑可以写在 linq 语句中,但对我来说理解起来会很复杂。
var items = new[]
{
new Foo{id=1,parentid = 0, name="bar1"},
new Foo{id=2,parentid = 1, name="bar2"},
new Foo{id=3,parentid = 2, name="bar3"},
new Foo{id=4,parentid = 0, name="bar12"},
new Foo{id=5,parentid = 1, name="bar13"},
new Foo{id=6,parentid = 2, name="bar14"},
new Foo{id=7,parentid = 2, name="bar3"}
};
var groups = items.GroupBy(item => item.parentid).ToList();
var firstLevelElements = items.Where(item => item.name == "bar1");
List<Foo> secondLevelElements = new List<Foo>();
foreach (var firstLevelElement in firstLevelElements)
{
secondLevelElements.AddRange(groups[firstLevelElement.id]
.Where(item => item.name == "bar2"));
}
List<Foo> thirdLevelElements = new List<Foo>();
foreach (var secondLevelElement in secondLevelElements)
{
thirdLevelElements.AddRange(groups[secondLevelElement.id]
.Where(item => item.name == "bar3"));
}