我有一个复杂的表达产生的ilookup。假设这是一个姓氏的查找。 (在我们简单的世界模型中,姓氏是独一无二的)

ILookup<string, Person> families;

现在,我有两个疑问,我对如何构建感兴趣。

首先,如何用姓氏过滤?

var germanFamilies = families.Where(family => IsNameGerman(family.Key));

但在这儿, germanFamilies 是一个 IEnumerable<IGrouping<string, Person>>;如果我打电话 ToLookup() 在上面,我最好敢打赌会得到一个 IGrouping<string, IGrouping<string, Person>>. 。如果我想变得聪明并打电话 SelectMany 首先,我最终要做很多不必要的工作。您如何轻松地将此枚举转换为查找?

其次,我只想看到成年人的查找。

var adults = families.Select(family =>
         new Grouping(family.Key, family.Select(person =>
               person.IsAdult())));

在这里,我面临两个问题: Grouping 类型不存在(除非是内部内部类 Lookup),即使确实如此,我们也会讨论上述问题。

因此,除了完全实施iLookup和iGroup界面,或者使计算机做愚蠢的工作(重组已经分组的内容)之外,是否有一种方法可以改变现有的ilookup来生成我错过的新的iLookup?

有帮助吗?

解决方案

(我将假设您实际上想用姓氏过滤,给定查询。)

您无法修改任何实现 ILookup<T> 我知道。当然有可能 实施 ToLookup 带着不变的查找, ,正如您清楚地知道:)

你什么 可以 但是,要更改以使用 Dictionary<string, List<Person>>:

var germanFamilies = families.Where(family => IsNameGerman(family.Key))
                             .ToDictionary(family => family.Key,
                                           family.ToList());

该方法还适用于您的第二个查询:

var adults = families.ToDictionary(family => family.Key,
                                   family.Where(person => persion.IsAdult)
                                         .ToList());

虽然仍然在做 少量 比我们想象的要多的工作要多,还不错。

编辑:评论中与ANI的讨论值得阅读。基本上,无论如何,我们已经将每个人都迭代了 - 因此,如果我们假设o(1)词典查找和插入,那么我们实际上并不好 在时间复杂性方面 使用现有的查找而不是扁平化:

var adults = families.SelectMany(x => x)
                     .Where(person => person.IsAdult)
                     .ToLookup(x => x.LastName);

在第一种情况下,我们可能会使用现有的分组,例如:

// We'll have an IDictionary<string, IGrouping<string, Person>>
var germanFamilies = families.Where(family => IsNameGerman(family.Key))
                             .ToDictionary(family => family.Key);

这样,这可能会更加有效(如果每个家庭中有很多人),但意味着我们正在使用“脱节”的分组。我相信这实际上还可以,但是由于某种原因,它在我的嘴里留下了些许奇怪的味道。作为 ToLookup 实现查询,很难看到它实际上可能出错...

其他提示

对于您的第一个查询,如何实施自己的 FilteredLookup 能够利用另一个 ILookup ?
(感谢Jon Skeet的提示)

public static ILookup<TKey, TElement> ToFilteredLookup<TKey, TElement>(this ILookup<TKey, TElement> lookup, Func<IGrouping<TKey, TElement>, bool> filter)
{
    return new FilteredLookup<TKey, TElement>(lookup, filter);
}

FilteredLookup 班级是:

internal sealed class FilteredLookup<TKey, TElement> : ILookup<TKey, TElement>
{
    int count = -1;
    Func<IGrouping<TKey, TElement>, bool> filter;
    ILookup<TKey, TElement> lookup;

    public FilteredLookup(ILookup<TKey, TElement> lookup, Func<IGrouping<TKey, TElement>, bool> filter)
    {
        this.filter = filter;
        this.lookup = lookup;
    }

    public bool Contains(TKey key)
    {
        if (this.lookup.Contains(key))
            return this.filter(this.GetGrouping(key));
        return false;
    }

    public int Count
    {
        get
        {
            if (count >= 0)
                return count;
            count = this.lookup.Where(filter).Count();
            return count;
        }
    }

    public IEnumerable<TElement> this[TKey key]
    {
        get
        {
            var grp = this.GetGrouping(key);
            if (!filter(grp))
                throw new KeyNotFoundException();
            return grp;
        }
    }

    public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
    {
        return this.lookup.Where(filter).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    private IGrouping<TKey, TElement> GetGrouping(TKey key)
    {
        return new Grouping<TKey, TElement>(key, this.lookup[key]);
    }
}

和分组:

internal sealed class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
    private readonly TKey key;
    private readonly IEnumerable<TElement> elements;

    internal Grouping(TKey key, IEnumerable<TElement> elements)
    {
        this.key = key;
        this.elements = elements;
    }

    public TKey Key { get { return key; } }

    public IEnumerator<TElement> GetEnumerator()
    {
        return elements.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

因此,基本上您的第一个查询将是:

var germanFamilies = families.ToFilteredLookup(family => IsNameGerman(family.Key));

这使您可以避免重新填充过滤器,或者创建新的词典(以及再次放哈希键)。

对于第二个查询,想法将是相似的,您应该只创建一个类似的类,而不是整体上过滤 IGrouping 但是对于 IGrouping.

只是一个想法,也许不能比其他方法更快:)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top