根据包含ID的列表选择项目

时间:2018-04-10 10:35:40

标签: c# linq

我有一个Items列表,每个都包含一个Type integer字段。

我想过滤我的列表,只获得与给定的整数列表匹配的项目。

我现在的代码有效,但我知道它可以优化。

Class Item
{
    int ID;

    //Other fields & methods that are irrelevant here
}

//Selection method
IEnumerable<Item> SelectItems(List<Item> allItems, List<int> toSelect)
{
     return allItems.Where(x => toSelect.Contains(x.ID));
}

我遇到的问题是我遍历allItems并在每次迭代中迭代toSelect

我觉得有可能更有效但我不知道如何用Linq实现这一目标。

这可能也是一个已被问到的问题,因为我不知道这是如何用英语调用的。这感觉有点愚蠢,因为我不知道如何在搜索引擎中正确地制定它。

2 个答案:

答案 0 :(得分:5)

您可以使用效率更高的Join,因为它使用基于集合的方法:

var selectedItems = from item in allItems
                    join id in toSelect
                    on item.Id equals id
                    select item;
return selectedItems;

另一种更有效的方法是使用HashSet<int>而不是列表:

IEnumerable<Item> SelectItems(List<Item> allItems, HashSet<int> toSelect)
{
     return allItems.Where(x => toSelect.Contains(x.ID));
}

答案 1 :(得分:3)

有两种方法可以解决这个问题。

目前,您有O(N×M)表现(其中N的大小为allItemsM的大小为toSelect

如果您只是想轻松地减少,那么您可以通过创建O(N)+O(M)的哈希集将其缩减为toSelect

var matches = new HashSet<int>(toSelect);
return allItems.Where(x => matches.Contains(x.ID));

但是,这仍然会由N占据主导地位 - allItems的大小。

更好的长期方法可能是预先索引数据(以及保持索引Id。因此,allItems不是List<T>,而是Dictionary<int, T>。请注意,构建字典可能很昂贵,因此您不希望每次要搜索时都这样做:关键是在开始时执行此操作(并保持维护) 。 然后这变为O(M)toSelect的大小,通常很小),因为字典查找是O(1)

IEnumerable<Item> SelectItems(Dictionary<int, Item> allItems, List<int> toSelect)
{
     foreach(var id in toSelect)
     {
         if (allItems.TryGetValue(id, out var found))
             yield return found;
     }
}

(不需要预先哈希toSelect,因为我们没有检查Contains