我希望用可选键创建快速查找(字典?),例如,让我说我有3个键:" first_name"," last_name" ,"邮政编码"
所以我希望能够做到以下(伪代码):
GetValue(first_name) -- would return a list of everyone with that first name
GetValue(first_name, last_name) -- would return a list of everyone with that first name & last name
GetValue(zipcode, first_name) -- would return a list of everyone with that first_name in the specified zipcode
我应该能够查询这些键的所有排列。您将使用什么数据结构?你会如何实现这个?
答案 0 :(得分:5)
您仍然可以使用常规词典,其中键可以是这样的自定义类型:
public class CompositeKey
{
public CompositeKey(string firstName, string lastName, string zipCode)
{
FirstName = firstName;
LastName = lastName;
ZipCode = zipCode;
}
public string FirstName { get; }
public string LastName { get; }
public string ZipCode { get; }
}
现在,我将覆盖Equals
上的GetHashCode
和CompositeKey
,以提供使复合键唯一的内容,以便Dictionary<TKey, TValue>
能够存储唯一的复合键。
最后,我可以这样查询字典:
var value = dict[new CompositeKey(firstName: "Matías", lastName: "Fidemraizer" )];
OP在一些评论中提到了这个问题:
我考虑过这种方法,但你会如何查询字典? for&#34; FirstName =&#34; Matias&#34;仅?
由于您要覆盖Equals
和GetHashCode
,因此您可以将所有组合添加为整个词典中的键,并且它们可以共存于那里:
Person person = new Person { /* Set members here */ }
// Note that I'll add many keys that have the same value
dict.Add(new CompositeKey(name: "Matías"), person);
dict.Add(new CompositeKey(lastName: "Fidemraizer"), person);
dict.Add(new CompositeKey(firstName: "Matías", lastName: "Fidemraizer"), person);
每个密钥都会产生不同的哈希代码,因此它们可以共存于同一个字典中,并且它们将提供一个强大的工具来查询许多标准和标准组合。
其他方法可能是使用多个字典,其中键是使用某些约定的整个值的连接,值是整个类的实例:
Dictionary<string, Person> names = new Dictionary<string, Person>();
names.Add("matias", new Person { /* Set members here */ });
Dictionary<string, Person> names = new Dictionary<string, Person>();
names.Add("matias:fidemraizer", new Person { /* Set members here */ });
// And so on, for every criteria you want to search...
稍后,您将实现一个代理,以根据给定的条件确定要查询的字典。
实际上你应该看看Redis,它是一个具有复杂数据结构的键值存储,如散列,集合,排序集等等。也就是说,您可以集中缓存并使用Redis进行分发,并且许多应用程序都可以使用缓存。
使用和安装非常简单(它的可执行程序小于10MB ......)。
他说:
具有相同名字的第二个人呢?
如果OP需要考虑这种情况(是的,它不是一个例外情况,所以它值得努力考虑它!),似乎OP需要将数据存储在字典,其中键是整个复合键,值应为List<Person>
,HashSet<Person>
或甚至LinkedList<Person>
。
此外,这意味着一个键(插槽)可以存储许多人,而像这样的查询会获得名字 {{1}的人总会返回"Matías"
(list,hash,linkedlist ...)的实现,其中整个返回的集合将是找到的人:
IEnumerable<Person>
此外,这种增强的方法还有另一个问题。当您使用KeyValuePair<CompositeKey, ISet<Person>> result;
if(dictionary.TryGetValue(new CompositeKey(firstName: "Matías"), out result))
{
// I've got either one or many results and I'll decide what to do in
// that case!
}
之类的复合键进行查询时,整个字典存储可能存储的人名不仅仅是new CompositeKey(firstName: "Matías")
名字的人,那么您将获得"Matías"
,{{1 }或ISet<Person>
。
获得一个或多个结果的第一次搜索具有复杂度IList<Person>
(常量时间),因为整个复合键基于其哈希码存储,但返回第一次搜索的结果不再是字典,任何针对它们的搜索都将是O(N)(您获得的项目越多,查找结果的时间就越多)< /强>
因此,如果结果大于LinkedList<Person>
,您似乎需要消除结果的歧义,并且可以通过提供超过{$ 1}}的复合键来执行另一个O(1)
搜索名字或某些人类用户(或人工智能......)需要手动选择其中一个结果。
总结:
1
复杂度的操作):< / LI>
O(1)
O(1)
答案 1 :(得分:3)
您可以使用3 Lookup
s:
var FirstNamesLookup = data.ToLookup(x => Tuple.Create(x.FirstName), x => x);
var FirstAndLastLookup = data.ToLookup(x => Tuple.Create(x.FirstName, x.LastName), x => x);
var FirstAndZipLookup = data.ToLookup(x => Tuple.Create(x.FirstName, x.zipCode), x => x);
具有特定FirstName的所有记录:
var matches = FirstNamesLookup[Tuple.Create("SomeName")].ToList();
具有特定FirstName和LastName的所有记录:
var matches = FirstAndLastLookup[Tuple.Create("SomeName", "SomeLastName")].ToList();
第三种情况也一样。
答案 2 :(得分:2)
您应该只使用任何泛型集合类型,并使用LINQ进行查找:
var addresses = Enumerable.Empty<Address>();
// would return a list of everyone with that first name
addresses.Where(x => x.FirstName == "firstname");
// would return a list of everyone with that first name & last name
addresses.Where(x => x.FirstName == "firstname" && x.LastName == "lastname");
// would return a list of everyone with that first_name in the specified zipcode
addresses.Where(x => x.FirstName == "firstname" && x.ZipCode == "zipcode");
答案 3 :(得分:0)
基于这里的所有想法以及我被限制使用.NET 2.0的事实,我这样做了。包括它只是为了完整性,以防有人再次遇到这个问题。 [不会将此标记为答案,因为它基于上述@Matias的许多想法,因此将其作为对最终解决方案贡献最大的答案]:
public class PersonKey {
public string FirstName { get; private set; }
public string LastName { get; private set; }
public int Zipcode { get; private set; }
public PersonKey() {
FirstName = null;
LastName = null;
Zipcode = int.MinValue;
}
public PersonKey(int Zipcode, string FirstName) : this() {
this.FirstName = FirstName;
this.Zipcode = Zipcode;
}
public PersonKey(string LastName, string FirstName) : this() {
this.FirstName = FirstName;
this.LastName = LastName;
}
public PersonKey(int Zipcode, string LastName, string FirstName) {
this.Zipcode = Zipcode;
this.LastName = LastName;
this.FirstName = FirstName;
}
public List<string> KeyList {
get {
var keyLst = new List<string>();
if (!String.IsNullOrEmpty(FirstName))
keyLst.Add("FirstName:" + FirstName);
if (!String.IsNullOrEmpty(LastName))
keyLst.Add("LastName:" + LastName);
if (Zipcode != int.MinValue)
keyLst.Add("Zipcode:" + Zipcode);
return keyLst;
}
}
public string Key {
get {
return MakeKey(KeyList.ToArray());
}
}
public List<string[]> AllPossibleKeys {
get {
return CreateSubsets(KeyList.ToArray());
}
}
List<T[]> CreateSubsets<T>(T[] originalArray) {
List<T[]> subsets = new List<T[]>();
for (int i = 0; i < originalArray.Length; i++) {
int subsetCount = subsets.Count;
subsets.Add(new T[] { originalArray[i] });
for (int j = 0; j < subsetCount; j++) {
T[] newSubset = new T[subsets[j].Length + 1];
subsets[j].CopyTo(newSubset, 0);
newSubset[newSubset.Length - 1] = originalArray[i];
subsets.Add(newSubset);
}
}
return subsets;
}
internal string MakeKey(string[] possKey) {
return String.Join(",", possKey);
}
然后我创建一个这样的缓存:
//declare the cache
private Dictionary<string, List<Person>> _lookup = new Dictionary<string, List<Person>>();
当我从数据库中读取记录时,我将其存储在缓存中,如下所示:
var key = new PersonKey(person.ZipCode, person.LastName, person.FirstName);
List<Person> lst;
foreach (var possKey in key.AllPossibleKeys) {
var k = key.MakeKey(possKey);
if (!_lookup.TryGetValue(k, out lst)) {
lst = new List<Person>();
_lookup.Add(k, lst);
}
lst.Add(person);
}
从缓存中查找非常简单:
List<Person> lst;
var key = new PersonKey(lastName, firstName); //more constructors can be added in PersonKey
_lookup.TryGetValue(key.Key, out lst);