Frage

Ich habe eine ILookup durch einigen komplizierten Ausdruck erzeugt bekommt. Lassen Sie uns sagen, dass es ein Nachschlag von Menschen, die von den Nachnamen. (In unserem simplen Weltmodell, sind letzte Namen eindeutig von der Familie)

ILookup<string, Person> families;

Jetzt habe ich zwei Abfragen habe ich interessiert bin, wie zu bauen.

Erstens, wie würde ich nach Nachnamen filtern?

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

Aber hier germanFamilies ist ein IEnumerable<IGrouping<string, Person>>; wenn ich ToLookup() auf ihn nennen, würde ich beste Wette wäre eine IGrouping<string, IGrouping<string, Person>> bekommen. Wenn ich versuche, smart und Anruf SelectMany sein erstes würde ich mit dem Computer am Ende eine Menge unnötiger Arbeit zu tun. Wie würden Sie diese Aufzählung in eine Lookup leicht umwandeln?

Zweitens, ich möchte ein Lookups von nur für Erwachsene erhalten.

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

Hier bin ich mit zwei Problemen konfrontiert:. Die Grouping Typ existiert nicht (außer als interne innere Klasse von Lookup) und selbst wenn es so wäre hätten wir das oben diskutierte Problem

Also, abgesehen von den ILookup Implementierung und IGrouping Schnittstellen vollständig oder den Computer machen Sie dumme Mengen an Arbeit (Umgruppierung was bereits gruppiert ist), ist es eine Möglichkeit, bestehende ILookups zu verändern, um neue zu erzeugen, die ich verpasst?

War es hilfreich?

Lösung

(Ich werde Sie tatsächlich zu übernehmen, um Filter nach Nachnamen wollte, da Ihre Anfrage.)

Sie können keine Implementierung von ILookup<T> ändern, dass ich bin mir dessen bewusst. Es ist sicherlich möglich, implementieren ToLookup mit einem unveränderlichen Lookup , wie Sie klar bewusst sind:)

Was Sie könnte Sie ist jedoch ändern, um eine Dictionary<string, List<Person>> zu verwenden:

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

Dieser Ansatz funktioniert auch für Ihre zweite Abfrage:

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

Während das ist immer noch ein Bit tun mehr Arbeit, als wir notwendig könnten denken, es ist nicht so schlimm.

EDIT: Die Diskussion mit Ani in den Kommentaren ist lesenswert. Im Grunde sind wir bereits Iterieren sowieso über jeden Menschen sein werden - also, wenn wir davon ausgehen, O (1) Wörterbuchsuche und Insertion, sind wir eigentlich nicht besser in Bezug auf die Zeit-Komplexität mit dem bestehenden Nachschlag als Abflachen:

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

Im ersten Fall, könnten wir möglicherweise verwenden die bestehenden Gruppierung, wie folgt aus:

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

Das ist dann möglicherweise viel effizienter (wenn wir viele Menschen in jeder Familie haben), aber Mittel, die wir verwenden Gruppierungen „aus dem Zusammenhang gerissen“. Ich glaube, das ist eigentlich in Ordnung, aber es hinterlässt einen etwas seltsam Geschmack in meinem Mund, aus irgendeinem Grunde. Als ToLookup die Abfrage materialisiert, es ist schwer zu sehen, wie es wirklich schief gehen könnte aber ...

Andere Tipps

Für Ihre erste Abfrage, was über die eigene FilteredLookup Lage Implementierung Vorteil zu nehmen von einem anderen ILookup kommen?
(Danke an Jon Skeet für den Hinweis)

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

Mit FilteredLookup Klasse Wesen:

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

und Gruppierung:

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

Also im Grunde Ihre erste Abfrage wird sein:

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

Auf diese Weise können Sie wieder Abflachen-Filterung-ToLookup, oder die Schaffung eines neuen Wörterbuch vermeiden (und so Tasten wieder Hashing).

Für die zweite Abfrage der Idee ähnlich sein wird, sollten Sie nur eine ähnliche Klasse erstellen nicht für die gesamte IGrouping Filterung, sondern für die Elemente der IGrouping.

Nur eine Idee, vielleicht könnte es nicht schneller sein als andere Methoden:)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top