Em C #, há out-of-the-box maneira de construir uma tabela de referência de 3 vias?
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]
emyTable[Names.Adam]
emyTable[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 ...
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.