In C #, c'è out-of-the-box modo per costruire una tabella di ricerca a 3 vie?

StackOverflow https://stackoverflow.com/questions/515887

  •  21-08-2019
  •  | 
  •  

Domanda

Ho un in-memory "tavolo" che potrebbe sembra qualcosa di simile:

Favorite#  Name        Profession
---------  ----------  ------------------
3          Names.Adam  Profession.Baker
9          Names.Bob   Profession.Teacher
7          Names.Carl  Profession.Coder
7          Names.Dave  Profession.Miner
5          Names.Fred  Profession.Teacher

E quello che voglio fare, è fare ricerche rapide ed efficienti, utilizzando uno qualsiasi dei 3 campi. In altre parole, io voglio:

  • myTable[3] e myTable[Names.Adam] e myTable[Professions.Baker] a tutti ritorno {3,Names.Adam,Profession.Baker}
  • myTable[Profession.Teacher] per ritornare sia {9,Names.Bob,Profession.Teacher} e {5,Names.Fred,Profession.Teacher}.

La tabella è costruita durante il runtime, in base alle azioni dell'utente, e non può essere memorizzato in un database dal momento che viene utilizzato nelle sezioni in cui la connettività di database non può essere garantita.

In questo momento, "semplicemente" (ah!) Conservare questo utilizzando 3 uber-Dizionari, ogni digitato utilizzando una delle colonne (FavoriteNumber, nome, professione), e ogni valore nel super-Dizionari azienda 2 Dizionari che sono si calettata con ciascuno dei rimanenti colonne (quindi i valori nel super-dizionario "Nome" sono di tipo Dictionary<FavoriteNumber,Profession[]> e Dictionary<Profession, FavoriteNumber[]>

Questo richiede 2 ricerche in 2 dizionari, e un'altra traversata di una matrice (che solitamente contiene 1 o 2 elementi.)

Qualcuno può suggerire un modo migliore per fare questo? Non mi dispiace spendere memoria aggiuntiva, dal momento che il tavolo è probabile che sia piccolo (non più di 20 voci), ma io sono disposto a sacrificare un po 'di CPU per rendere il codice più facilmente mantenibile ...

È stato utile?

Soluzione

Non proprio però usando un dizionario, ma se si crea una collezione di classi come questo

class Person {
    public int FavoriteNumber;
    public string Name;
    public string Profession;
}

è possibile utilizzare LINQ per la ricerca delle collezioni.

IList<Person> people = /* my collection */;
var selectedPeople = people.Where(p => p.FavoriteNumber = 3);
var selectedPeople2 = people.Where(p => p.Name == "Bob");
var selectedPeople3 = people.Where(p => p.Profession = "Teacher");

o se si preferisce la sintassi LINQ normale

var selectedPeople4 = from p in people
                      where p.Name == "Bob"
                      select p;

Ciascuna di queste variabili selectedPeople sarà tipizzato come IEnumerable<Person> e si può utilizzare un ciclo per la ricerca in loro.

Altri suggerimenti

Per 20 righe, basta usare linear scansione -. sarà il più efficiente in ogni modo

Per i set più grandi; hzere è un approccio utilizzando LINQ di ToLookup e l'indicizzazione in ritardo:

public enum Profession {
    Baker, Teacher, Coder, Miner
}
public class Record {
    public int FavoriteNumber {get;set;}
    public string Name {get;set;}
    public Profession Profession {get;set;}
}
class Table : Collection<Record>
{
    protected void Rebuild()
    {
        indexName = null;
        indexNumber = null;
        indexProfession = null;
    }
    protected override void ClearItems()
    {
        base.ClearItems();
        Rebuild();
    }
    protected override void InsertItem(int index, Record item)
    {
        base.InsertItem(index, item);
        Rebuild();
    }
    protected override void RemoveItem(int index)
    {
        base.RemoveItem(index);
        Rebuild();
    }
    protected override void SetItem(int index, Record item)
    {
        base.SetItem(index, item);
        Rebuild();
    }
    ILookup<int, Record> indexNumber;
    ILookup<string, Record> indexName;
    ILookup<Profession, Record> indexProfession;
    protected ILookup<int, Record> IndexNumber {
        get {
            if (indexNumber == null) indexNumber = this.ToLookup(x=>x.FavoriteNumber);
            return indexNumber;
        }
    }
    protected ILookup<string, Record> IndexName {
        get {
            if (indexName == null) indexName = this.ToLookup(x=>x.Name);
            return indexName;
        }
    }
    protected ILookup<Profession, Record> IndexProfession {
        get {
            if (indexProfession == null) indexProfession = this.ToLookup(x=>x.Profession);
            return indexProfession;
        }
    }
    public IEnumerable<Record> Find(int favoriteNumber) { return IndexNumber[favoriteNumber]; }
    public IEnumerable<Record> Find(string name) { return IndexName[name]; }
    public IEnumerable<Record> Find(Profession profession) { return IndexProfession[profession]; }
}

Credo che il modo per farlo è quello di scrivere il proprio oggetto che ha

public ICollection<Record> this[int] { get; }
public ICollection<Record> this[Profession] { get; }
public ICollection<Record> this[Names] { get; }

dove record è una classe che contiene gli elementi.

Internamente, si mantiene una lista e ogni indicizzatore non List.FindAll () per ottenere quello che ti serve.

Niente out-of-the-box (tranne forse un DataTable). Tuttavia, può essere realizzato in modo più semplice che quello che hai:

Creare una classe per contenere i dati:

class PersonData {
   public int FavoriteNumber;
   public string Name;
   public string Profession;
}

Quindi tenere 3 dizionari che puntano allo stesso riferimento:

PersonData personData = new PersonData();
Dictionary<int, PersonData> ...;
Dictionary<string, PersonData> ...;
Dictionary<string, PersonData> ...;

io consiglierei di incapsulare tutto questo in una classe di facciata che nasconde i dettagli di implementazione.

Potresti utilizzare un sqlite database come il sostegno? Con SQLite si ha anche la possibilità di costruire un db in memoria.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top