【问题标题】:Efficient Hierarchal Linq query using multiple properties使用多个属性的高效 Hierarchal Linq 查询
【发布时间】:2012-08-08 13:07:22
【问题描述】:

我收集了相当多的foo { int id, int parentid, string name}

我希望收集foo 对象的列表,其中对象的名称为“bar3”,并且是名为“bar2”的对象的子对象,该对象是 ID 为 @ 的对象的子对象987654323@.

我应该使用什么样的集合(我一直在使用查找和字典,但没有取得很大成功),我应该如何编写它以从中获得高效的功能?大约有 30K foo 对象,我的方法快要窒息了。

谢谢!

【问题讨论】:

  • 听起来您正在尝试实现树形数据结构。你说的效率更高是什么意思?什么窒息?您是内存不足还是正在尝试查找。
  • 绝对是一个树形数据结构,但问题是当我尝试运行它时,它每次迭代都会挂起 3-5 分钟,并且由于它运行了数百次,它需要更高效。我正在尝试使用索引,但 C# 集合中似乎不存在复合索引的概念。
  • 如果是树形数据结构,为什么不创建树形数据结构呢?既然可以拥有children,为什么还要拥有parentID
  • 你使用什么样的查询,具体的数据结构是什么?
  • 结构不可变;我正在链接到具有已定义对象的 API。它定义了一个 id、一个 parentid 和一个名字,等等。 parentid 和 name 理论上是唯一键,id 也是。

标签: c# linq collections


【解决方案1】:

如果我真的必须坚持 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 对象来自哪里),并为每个项目创建一个parentidname 的元组。这用作查找的键,因此对于每个 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”的元素。

因此,对于父 ID 为 1 且名称为 bar2 的每个项目,我们会找到以第一个项目的 ID 作为其父 ID 和名称 bar3 的每个项目。这就是我们想要的。

【讨论】:

  • 这看起来很有希望。你能解释一下这里发生了什么吗?
  • 好的,加了一点解释。
【解决方案2】:

看看这个:QuickGraph 我从未真正使用过它,但它似乎有据可查。 或者你可以试试C5 Generic Collection Library

我从这个tread得到这个

【讨论】:

    【解决方案3】:

    我可以建议您先按 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"));
                }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-05
      相关资源
      最近更新 更多