В C # есть ли готовый способ построить таблицу поиска с 3 путями? - PullRequest
3 голосов
/ 05 февраля 2009

У меня есть «таблица» в памяти, которая может выглядеть примерно так:

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

И я хочу сделать быстрый и эффективный поиск, используя любое из 3 полей. Другими словами, я хочу:

  • myTable[3] и myTable[Names.Adam] и myTable[Professions.Baker] на все возвраты {3,Names.Adam,Profession.Baker}
  • myTable[Profession.Teacher] для возврата {9,Names.Bob,Profession.Teacher} и {5,Names.Fred,Profession.Teacher}.

Таблица создается во время выполнения в соответствии с действиями пользователя и не может быть сохранена в базе данных, поскольку она используется в разделах, в которых не может быть гарантировано подключение к базе данных.

Прямо сейчас я "просто" (ха!) Сохраняю это, используя 3 uber-словаря, каждый из которых имеет ключ, используя один из столбцов (FavoriteNumber, Name, Profession), и каждое значение в uber-Dictionaries содержит 2 словаря, которые сами по себе с каждым из оставшихся столбцов (поэтому значения в uber-словаре «Name» имеют тип Dictionary<FavoriteNumber,Profession[]> и Dictionary<Profession, FavoriteNumber[]>

Для этого требуется 2 поиска в 2 словарях и еще один обход массива (который обычно содержит 1 или 2 элемента.)

Может кто-нибудь предложить лучший способ сделать это? Я не возражаю тратить дополнительную память, так как таблица, вероятно, будет небольшой (не более 20 записей), но я готов пожертвовать небольшим ЦП, чтобы сделать его более удобным для сопровождения кода ...

Ответы [ 5 ]

9 голосов
/ 05 февраля 2009

Не совсем с использованием словаря, но если вы создаете коллекцию классов, подобных этой

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

Вы можете использовать LINQ для поиска в коллекциях.

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

или, если вы предпочитаете нормальный синтаксис LINQ

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

Каждая из этих selectedPeople переменных будет напечатана как IEnumerable<Person>, и вы можете использовать цикл для их поиска.

6 голосов
/ 05 февраля 2009

Для 20 строк просто используйте линейное сканирование - это будет наиболее эффективно во всех отношениях.

Для больших наборов; hzere - это подход, использующий LINQ ToLookup и отложенную индексацию:

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]; }
}
5 голосов
/ 05 февраля 2009

Я думаю, что способ сделать это - написать свой собственный объект, который имеет

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

где запись - это класс, содержащий ваши элементы.

Внутренне вы храните список, а каждый индексатор выполняет List.FindAll (), чтобы получить то, что вам нужно.

4 голосов
/ 05 февраля 2009

Ничего нестандартного (кроме, возможно, DataTable). Тем не менее, это может быть выполнено более простым способом, чем то, что у вас есть:

Создать класс для хранения данных:

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

Затем сохраните 3 словаря, которые указывают на одну и ту же ссылку:

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

Я бы рекомендовал инкапсулировать все это в класс фасада, который скрывает детали реализации.

1 голос
/ 05 февраля 2009

Не могли бы вы использовать базу данных sqlite в качестве основы? С sqlite у вас даже есть возможность построить базу данных в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...