Em C #, há out-of-the-box maneira de construir uma tabela de referência de 3 vias?

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

  •  21-08-2019
  •  | 
  •  

Pergunta

Eu tenho uma "mesa" na memória que força é algo como isto:

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 o que eu quero fazer, é fazer pesquisas rápidas e eficientes, usando qualquer um dos 3 campos. Em outras palavras, eu quero:

  • myTable[3] e myTable[Names.Adam] e myTable[Professions.Baker] para todo o retorno {3,Names.Adam,Profession.Baker}
  • myTable[Profession.Teacher] para retornar tanto {9,Names.Bob,Profession.Teacher} e {5,Names.Fred,Profession.Teacher}.

A tabela é construída durante a execução, de acordo com as ações do usuário, e não pode ser armazenado em um banco de dados, uma vez que é utilizado em secções em que a conectividade de banco de dados não pode ser garantida.

Agora, eu "simplesmente" (hah!) Armazenar isso usando 3 uber-dicionários, cada introduzidos usando uma das colunas (FavoriteNumber, Nome, profissão), e cada valor nas uber-Dictionaries segurando 2 dicionários que são -se chaveado com cada uma das colunas restantes (de modo que os valores no "Nome" uber-dicionário são do tipo Dictionary<FavoriteNumber,Profession[]> e Dictionary<Profession, FavoriteNumber[]>

Isto requer 2 pesquisas em 2 dicionários, e outro transversal de uma matriz (que geralmente contém 1 ou 2 elementos.)

Alguém pode sugerir uma maneira melhor de fazer isso? Eu não me importo de passar memória extra, já que a mesa é provável que seja pequeno (não mais de 20 entradas), mas eu estou disposto a sacrificar um pouco da CPU para torná-lo código mais facilmente sustentável ...

Foi útil?

Solução

Não realmente no entanto usando um dicionário, mas se você criar uma coleção de classes como este

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

Você pode usar LINQ para procurar as colecções.

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

ou se você preferir o LINQ normal de sintaxe

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

Cada uma destas variáveis ??selectedPeople será digitado como IEnumerable<Person> e você pode usar um loop para procurar por eles.

Outras dicas

Para 20 linhas, basta usar linear digitalização -. ele vai ser o mais eficiente em todos os sentidos

Para maiores conjuntos; hzere é uma abordagem usando ToLookup do LINQ e indexação adiada:

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

Eu acho que a maneira de fazer isso é escrever seu próprio objeto que tem

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

onde registro é uma classe que detém os seus elementos.

Internamente, você mantém uma lista e cada indexador faz List.FindAll () para obter o que você precisa.

Nada out-of-the-box (exceto, talvez, um DataTable). No entanto, ele pode ser realizado de uma forma mais simples que o que você tem:

Crie uma classe para armazenar os dados:

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

Em seguida, manter 3 dicionários que apontam para a mesma referência:

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

Eu recomendo encapsular tudo isso em uma classe de fachada que esconde os detalhes de implementação.

Você poderia usar um href="http://sqlite.phxsoftware.com/" rel="nofollow noreferrer"> banco de dados SQLite como o apoio? Com sqlite você ainda tem a opção de construir um db na memória.

scroll top