Question

J'ai un iLookup généré par une expression compliquée. Disons que c'est une recherche de personnes par nom. (Dans notre modèle mondial simpliste, noms de famille sont uniques par famille)

ILookup<string, Person> families;

Maintenant, j'ai deux questions, je suis intéressé par la façon de construire.

Tout d'abord, comment pourrais-je filtrer par nom?

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

Mais ici, germanFamilies est un IEnumerable<IGrouping<string, Person>>; si je l'appelle ToLookup() là-dessus, je ferais mieux serait obtenir un IGrouping<string, IGrouping<string, Person>>. Si j'essaie d'être intelligent et SelectMany premier appel que je finirais avec l'ordinateur faire beaucoup de travail inutile. Comment voulez-vous convertir cette énumération en une recherche facilement?

En second lieu, je voudrais obtenir un des adultes lookups.

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

Ici, je suis confronté à deux problèmes:. Le type de Grouping n'existe pas (sauf en tant que classe interne intérieure de Lookup), et même si elle ne nous aurions le problème discuté ci-dessus

Ainsi, en dehors de la mise en œuvre du iLookup et IGrouping interfaces complètement, ou que l'ordinateur ne sommes stupides de travail (regroupant ce qui a déjà été regroupés), est-il un moyen de modifier ILookups existants pour générer de nouvelles que je manqué?

Était-ce utile?

La solution

(je vais supposer que vous vouliez réellement filtrer par nom, compte tenu de votre requête.)

Vous ne pouvez pas modifier toute mise en œuvre de ILookup<T> que je suis au courant. Il est certainement possible de mettre en œuvre que vous êtes bien conscient ToLookup avec une immuable recherche ,:)

Qu'est-ce que vous peut ne, cependant, est de changer d'utiliser un Dictionary<string, List<Person>>:

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

Cette approche fonctionne aussi pour votre deuxième requête:

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

Alors que faire encore ya bit plus de travail que nous pourrions penser nécessaire, il est pas trop mal.

EDIT: La discussion avec Ani dans les commentaires mérite d'être lu. En gros, nous allons déjà être sur toute personne itérer de toute façon - si nous supposons O (1) Dictionnaire recherche et insertion, nous sommes en fait pas mieux en termes de temps complexité en utilisant l'existant recherche d'aplatissement:

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

Dans le premier cas, on pourrait utiliser le groupement existant, comme celui-ci:

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

C'est donc potentiellement beaucoup plus efficace (si nous avons beaucoup de gens dans chaque famille), mais des moyens que nous utilisons des groupes « hors contexte ». Je crois que ce fait d'accord, mais il laisse un goût un peu étrange dans ma bouche, pour une raison quelconque. Comme ToLookup matérialise la requête, il est difficile de voir comment il pourrait effectivement aller mal si ...

Autres conseils

Pour votre première requête, que sur l'implémentation de votre propre FilteredLookup en mesure de tirer parti de venir d'un autre ILookup?
(Merci à Jon Skeet pour l'indice)

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);
}

être de classe 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]);
    }
}

et regroupement:

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();
    }
}

Donc, fondamentalement, votre première requête sera:

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

Cela vous permet d'éviter re-aplatir-filtrage ToLookup, ou la création d'un nouveau dictionnaire (et donc les clés de hachage à nouveau).

Pour la deuxième requête l'idée sera similaire, vous devez simplement créer une classe similaire ne filtre pas pour l'ensemble IGrouping mais pour les éléments du IGrouping.

Juste une idée, peut-être qu'il ne pouvait pas être plus rapide que les autres méthodes:)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top