在 C# 中,是否有开箱即用的方法来构建 3 路查找表?

发布于 2024-07-13 09:28:06 字数 1263 浏览 6 评论 0原文

我有一个内存中的“表”,可能看起来像这样:

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] 全部返回 < code>{3,Names.Adam,Profession.Baker}
  • myTable[Profession.Teacher] 返回两个 {9,Names.Bob,Profession.Teacher}{5,Names.Fred,Profession.Teacher}

该表是在运行时根据用户的操作构建的,并且不能存储在数据库中,因为它用在无法保证数据库连接的部分中。

现在,我“简单地”(哈!)使用 3 个 uber-Dictionaries 存储它,每个 uber-Dictionaries 使用其中一列(FavoriteNumber、Name、Profession)进行键控,并且 uber-Dictionaries 中的每个值都包含 2 个字典,这些字典本身是键控的其余各列(因此“Name”超级词典中的值的类型为 DictionaryDictionary

这需要在 2 个字典中进行 2 次查找,并再次遍历一个数组(通常包含 1 或 2 个元素)。

任何人都可以提出更好的方法来执行此操作,因为该表我不介意花费额外的内存?可能很小(不超过 20 个条目),但我愿意牺牲一点 CPU 来使其代码更容易维护......

I have an in-memory "table" that might looks something like this:

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

And what I want to do, is do quick and efficient lookups, using any of the 3 fields.
In other words, I want:

  • myTable[3] and myTable[Names.Adam] and myTable[Professions.Baker] to all return {3,Names.Adam,Profession.Baker}
  • myTable[Profession.Teacher] to return both {9,Names.Bob,Profession.Teacher} and {5,Names.Fred,Profession.Teacher}.

The table is built during runtime, according to the actions of the user, and cannot be stored in a database since it is used in sections in which database connectivity cannot be guaranteed.

Right now, I "simply" (hah!) store this using 3 uber-Dictionaries, each keyed using one of the columns (FavoriteNumber, Name, Profession), and each value in the uber-Dictionaries holding 2 Dictionaries which are themselves keyed with each of the remaining columns (so the values in the "Name" uber-dictionary are of the type Dictionary<FavoriteNumber,Profession[]> and Dictionary<Profession, FavoriteNumber[]>

This requires 2 lookups in 2 Dictionaries, and another traverse of an array (which usually holds 1 or 2 elements.)

Can anyone suggest a better way to do this? I don't mind spending extra memory, since the table is likely to be small (no more than 20 entries) but I'm willing to sacrifice a little CPU to make it more readily maintainable code...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

心房敞 2024-07-20 09:28:06

但实际上并不是使用字典,但如果您创建这样的类集合,

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,您可以使用循环来搜索它们。

Not really however using a dictionary, but if you create a collection of classes like this

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

you can use LINQ to search the collections.

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

or if you prefer the normal LINQ syntax

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

Each of these selectedPeople variables will be typed as IEnumerable<Person> and you can use a loop to search through them.

流年已逝 2024-07-20 09:28:06

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

For 20 rows, just use linear scanning - it will be the most efficient in every way.

For larger sets; hzere's an approach using LINQ's ToLookup and delayed indexing:

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]; }
}
半山落雨半山空 2024-07-20 09:28:06

我认为执行此操作的方法是编写自己的对象,其中

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

record 是保存元素的类。

在内部,您保留一个列表,每个索引器执行 List.FindAll() 来获取您需要的内容。

I think the way to do this is to write your own object that has

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

where record is a class that holds your elements.

Internally, you keep a List and each indexer does List.FindAll() to get what you need.

煮酒 2024-07-20 09:28:06

没有任何开箱即用的东西(可能除了数据表)。 不过,它可以通过比您所拥有的更简单的方式来完成:

创建一个类来保存数据:

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> ...;

我建议将所有这些封装到一个外观类中隐藏实现细节。

Nothing out-of-the-box (except perhaps a DataTable). Nevertheless, it can be accomplished in a more simple way that what you've got:

Create a class to hold the data:

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

Then keep 3 dictionaries that point to the same reference:

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

I'd recommend encapsulating all of this into a facade class that hides the implementation details.

情魔剑神 2024-07-20 09:28:06

您可以使用 sqlite 数据库作为支持吗? 使用 sqlite,您甚至可以选择构建内存数据库。

Could you use an sqlite database as the backing? With sqlite you even have the option of building an in-memory db.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文