C# collection indexed by property?
c#, collections, linq
Solution
You can build your own collection backed by a dictionary to accomplish this task. The idea is to store a delegate that takes a Person and returns a string by reading the Name property.
Here is a skeletal solution of such a collection:
public class PropertyMap<K,V> : ICollection<V> {
private readonly IDictionary<K,V> dict = new Dictionary<K,V>();
private readonly Func<V,K> key;
public PropertyMap(Func<V,K> key) {
this.key = key;
}
public void Add(V v) {
dict.Add(key(v));
}
// Implement other methods of ICollection
public this[K k] {
get { return dict[k]; }
set { dict[k] = value; }
}
}
Here is how to use it:
PropertyMap<string,Person> mp = new PropertyMap<string,Person>(
p => p.Name
);
mp.Add(p1);
mp.Add(p2);
Problem
A problem I often run into is needing to store a collection of objects in such a way that I can retrieve them by a particular field/property that is a unique "index" for that object. For example, I have a `Person` object for which the `name` field is a unique identifier, and I want to be able to retrieve from some collection of `Person` objects the `Person` whose `name="Sax Russell"`. In Java I usually accomplish this by using a `Map` where I actually want a `Set`, and always using the "index" field of the object as its key in the map, i.e. `peopleMap.add(myPerson.getName(), myPerson)`. I was thinking of doing the same thing in C# with `Dictionary`s, like this: ``` class Person { public string Name {get; set;} public int Age {get; set;} //... } Dictionary<string, Person> PersonProducerMethod() { Dictionary<string, Person> people = new Dictionary<string, Person>(); //somehow produce Person instances... people.add(myPerson.Name, myPerson); //... return people; } void PersonConsumerMethod(Dictionary<string, Person> people, List<string> names) { foreach(var name in names) { person = people[name]; //process person somehow... } } ``` However, this seems clumsy, and introduces a rather loose coupling between the keys of the `Dictionary` and its values; I implicitly depend on every producer of `Person` dictionaries using the `Name` property as the key under which to store each `Person`. I have no guarantee that the element at `people["Sax Russell"]` is actually a `Person` with `Name="Sax Russell"` unless I double-check every time I access the dictionary. Might there be some way to explicitly ensure that my collection of `Person` objects is indexed by name, using custom equality comparers and/or LINQ queries? It's important that lookup stay constant-time, which is why I can't just use `List.Find` or `Enumerable.Where`. I've tried using a `HashSet` and constructing it with an equality comparer that compares just the `Name` field of the objects it's given, but there doesn't seem to be any way to then retrieve the `Person` objects using just their name.