I have the following:
public class Animal
public int currentPopulation
public string name
public Animal(int currentPopulation, string name){
this.currentPopulation = currentPopulation;
this.name = name;
}
In another class I have:
public class MainClass
<List><Animal>animalList
...
lion = newAnimal(100, "Lion");
cat = newAnimal(20, "Cat");
dog = newAnimal(40, "Dog")
animalList.add(lion);
animalList.add(cat);
animalList.add(dog);
Every so often I have to fetch new data from a server and update the animal property, currentPopulation in the MainClass. Currently I'm doing this by the following:
public void UpdatePopulations(int lionPopulation, int catPopulation, int dogPopulation)
foreach(var item in animalList.where(n=>n.name=="Lion")){
item.currentPopulation = lionPopulation;
}
... and the same for the cat and dog.
I feel like my solution is bulky and I'm wondering if there is a cleaner way to update the objects from the list.
When working with list (which is a dynamic container) there is no way to find element without iterating over it.
One way you can make it more efficient is updating your List in one path and not using LINQ to find elements.
Something like this
foreach(vat animal in animalList)
{
if (animal.name == "Lion")
animal.currentPopulation = ...
else if (animal.name == "...")
animal.currentPopulation = ...
}
I'd suggest you use different data container than list.
Dictionary<string, Animal> animals will serve you better because than you can use animal name as update keys.
You can also differentiate with the help of 'enum' as shown below but you need to iterate through list to identify animal.
public enum AnimalName
{
Lion,
Cat,
Dog
}
public class Animal
{
public AnimalName Name { get; set; }
public int Population { get; set; }
public Animal(AnimalName name, int population)
{
Name = name;
Population = population;
}
}
public void UpdatePopulations(int lionPopulation, int catPopulation, int dogPopulation)
{
foreach (var animal in animals)
{
switch (animal.Name)
{
case AnimalName.Lion:
animal.Population = lionPopulation;
break;
case AnimalName.Cat:
animal.Population = catPopulation;
break;
case AnimalName.Dog:
animal.Population = dogPopulation;
break;
}
}
}
There is almost no benefit to convert the animalList to Dictionary, if you are going to update all of the animals anyway. Some improvement that I can think of is to accept IEnumerable instead, so you can freely update certain or all animals.
public void UpdatePopulations(IEnumerable<Animal> newAnimals)
{
var dictionary = newAnimals.ToDictionary<string, int>(a=>a.Name, a=>a.currentPopulation); // convert to dictionary, so that we have O(1) lookup during the search later. This process itself is O(n)
foreach(var animal in animalList) // this will be O(n)
{
if(dictionary.ContainsKey(animal.Name))
{
animal.currentPopulation = dictionary[animal.Name].currentPopulation;
}
}
}
public void UpdatePopulations(Dictionary<string, int> populations)
{
foreach(var population in populations)
foreach(var animal in animalList.Where(x => x.name == population.Key))
animal.currentPopulation = population.Value;
}
Usage:
variableName.UpdatePopulations(new Dictionary<string, int> {
["Lion"] = 1000,
["Cat"] = 2000,
["Dog"] = 3000
});
Related
How to find element in List class without looping ?
for example ordinary look up using Looping :
for(int i = 0; i < inventoryx.Player.items.Count; i++) {
if(inventoryx.Player.items[i].itemName == "Wood") {
Debug.log("You Find The Wood");
}
else {
Debug.Log("Can't Find The Wood");
}
}
items.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
// Make Class Item
public class item {
public string itemName;
public int itemID;
public string itemDesc;
public string itemIcon;
public GameObject itemModel;
public int itemTime;
public int hightprice;
public int stdprice;
public int itemStock;
public int harvest;
public RawTree rawTree;
public ItemType itemType;
public ItemProd itemProd;
public ItemLocation itemLocation;
public int Lvlunlock;
private string baseName;
public int itemExp;
public enum ItemType {
Raw,
Admirable,
Valuable
}
public enum RawTree {
BigTree,
SmallTree,
Field,
None
}
public enum ItemProd {
Corps,
Dairy,
JuiceJamMaker,
Kitchen,
Bakery,
CraftHouse,
ChickenCoop,
FishingSpotMountain,
CowPasture,
LunaMine,
PigPen,
FishingSpotLake,
TropicalWood,
SheepPasture,
FishingSpotSea,
Beebox,
HomeIndustry
}
public enum ItemLocation {
Home,
Orchard
}
public item (string name, int ID, string desc, int harvestx, int time, int stdpricex, int hightpricex, int stock, int Lvlunlockx, RawTree RawTree, ItemType type, ItemProd prod, string folderx, ItemLocation location, int Exp) {
itemName = name;
itemID = ID;
itemDesc = desc;
harvest = harvestx;
itemTime = time;
stdprice = stdpricex;
hightprice = hightpricex;
itemStock = stock;
Lvlunlock = Lvlunlockx;
rawTree = RawTree;
itemType = type;
itemProd = prod;
itemIcon = folderx;
itemLocation = location;
itemExp = Exp;
}
public item() {
}
}
Is there any idea to find an items without looping such as above ?
because using looping to find the elements data is take more memory to do it. If there is more than 100 items it will make it lag and take more time.
Thanks
To make this more efficient for many items you can consider creating a dictionary, mapping itemNames to items:
Dictionary<string, Item> itemNamesToItem = inventoryx.Player.items.ToDictionary(i => i.itemName, i => i);
Then you can access the items by name:
if (itemNamesToItem.ContainsKey("Wood"))
Debug.log("You Find The Wood");
else
Debug.Log("Can't Find The Wood");
Of course you can only do this if the itemNames are unique. And you probably should store this dictionary per player, maybe as property of the player, so you don't have to recreate it everytime you want to look up an item.
If the itemNames are not unique, you could consider making a dictionary mapping itemName to a list of items:
Dictionary<string, List<Item>> itemNamesToItem =
inventoryx.Player.items.GroupBy(i => i.itemName)
.ToDictionary(g => g.Key, g => g.ToList());
Using LINQ you can write
using Sytem.Linq;
...
bool hasWood = inventoryx.Player.items.Any(i => i.ItemName == "Wood");
Or, to find the index:
int index = items.FindIndex(i => i.ItemName == "Wood");
Which returns the index of the first item matching the condition, or -1 to indicate no matches found.
This will still be O(n) though, just like your loop, but it is a bit more terse and readable.
Also, C# offers a foreach statement for iterating through IEnumerable collections, this helps prevent off-by-one exceptions:
foreach (var item in inventoryx.Player.items)
{
if (item.ItemName == "Wood")
Debug.log("You Find The Wood");
}
I am having some trouble in querying an object array using LINQ. I want to retrieve all products that contains the value passed.
My Product class
public class Product
{
public int mProductId;
public string mProductName;
public string mProductColor;
public string mProductSize;
public string mProductStatus;
public string mProductCode;
public int ProductId{ get { return mProductId; }}
public string ProductName { get{return mProductName; }}
public string ProductColor { get{return mProductColor;} }
public string ProductSize { get{return mProductSize;} }
public string ProductStatus { get{return mProductStatus;} }
public string ProductCode {get { return mProductCode; }}
}
public class ProductList
{
public static Product[] mProductList = {
new Product { mProductId = Resource.Drawable.Product1,
mProductName = "Green Lumberjack Cap",
mProductColor = "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760439",
mProductStatus= "In Stock"},
new Product { mProductId = Resource.Drawable.Product2,
mProductName = "Square Bar stool",
mProductColor= "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760440",
mProductStatus= "In Stock"},
new Product { mProductId = Resource.Drawable.Product3,
mProductName = "Vitra bathroom Tile",
mProductColor= "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760539",
mProductStatus= "In Stock"},
};
private Product[] mProducts;
Random mRandom;
public ProductList ()
{
mProducts = mProductList;
}
// Return the number of photos in the photo album:
public int NumPhotos
{
get { return mProducts.Length; }
}
// Indexer (read only) for accessing a photo:
public Product this[int i]
{
get { return mProducts[i]; }
}
// Pick a random photo and swap it with the top:
public int RandomSwap()
{
// Save the photo at the top:
Product tmpProduct = mProducts[0];
// Generate a next random index between 1 and
// Length (noninclusive):
int rnd = mRandom.Next(1, mProducts.Length);
// Exchange top photo with randomly-chosen photo:
mProducts[0] = mProducts[rnd];
mProducts[rnd] = tmpProduct;
// Return the index of which photo was swapped with the top:
return rnd;
}
// Shuffle the order of the photos:
public void Shuffle ()
{
// Use the Fisher-Yates shuffle algorithm:
for (int idx = 0; idx < mProducts.Length; ++idx)
{
// Save the photo at idx:
Product tmpProduct = mProducts[idx];
// Generate a next random index between idx (inclusive) and
// Length (noninclusive):
int rnd = mRandom.Next(idx, mProducts.Length);
// Exchange photo at idx with randomly-chosen (later) photo:
mProducts[idx] = mProducts[rnd];
mProducts[rnd] = tmpProduct;
}
}
}
and my LINQ statement is
var result = from p in nProductList<Product>
where ( p.mProductName.Contains(query) || p.mProductColor.Contains(query))
select p;
I have also declared nProductList in my class as
public ProductList nProductList;
It will be really great to know what am I doing wrong.
Thank you
In order to get the where keyword syntax to work, your ProductList class must have a Where(Func<Product, bool>) method on it. Most lists get this automatically because they implement IEnumerable<>, and the System.Linq namespace has a Where() extension method which matches this signature.
You could make ProductList implement the IEnumerable<Product> interface, or make it extend a class like List<Product> which already implements that interface, or add your own Where() method. However, I'd personally suggest that you just expose mProductList as an IEnumerable<Product> via a public property getter, and change your consuming code to query against that.
The reason why your linq statement does not work is because you did not define where. Imagine the old style linq:
nProductList.Where(p=>p.mProductName.Contains(query) || p.mProductColor.Contains(query)).Select(p=>);
nProductList does not have Where(Func) defined so it does not work.
Normally for your ProductList there are two ways to implement. First way is to inherit from IEnumerable<Product> as ProductList : IEnumerable<Product>;
Second way is to create a member in ProductList and make it public like
public class ProductList
{
public IEnumerable<Product> Products {get; private set;}
...
}
Usually the preferable way between the two above will depends on is there more properties or more methods in your ProductList class. More methods goes to the first way because it's more like an expended method collection of IEnumerable class (like your example), while more properties goes to the seconding way as this is more like another class just having a list in it and something else.
I am keeping track of values in a console. Two people "duel" against each other and I was using a dictionary to keep the names recorded along with damage done.
var duels = new Dictionary<string, string>();
duels.Add("User1", "50");
duels.Add("User2","34");
I'm trying to store both users in the same dictionary row, so it could be verified as User1 is dueling against User2. This way if another duel started, it would not interfere with User1 or User2.
duels.Add("KeyUser1","KeyUser2","50","34",.../*Other attributes of the duel*/);
I need two keys so I can check where the user's damage will go. The damage will always go to the other key--vice versa.
What can I do to make this work?
Thank you.
public class Duel
{
public string User1 {get; protected set;}
public string User2 {get; protected set;}
public Duel(string user1, string user2)
{
User1 = user1;
User2 = user2;
}
public HashSet<string> GetUserSet()
{
HashSet<string> result = new HashSet<string>();
result.Add(this.User1);
result.Add(this.User2);
return result;
}
//TODO ... more impl
}
Let's make some duels. CreateSetComparer allows the dictionary to use the values of the set for equality testing.
List<Duel> duelSource = GetDuels();
Dictionary<HashSet<string>, Duel> duels =
new Dictionary<HashSet<string>, Duel>(HashSet<string>.CreateSetComparer());
foreach(Duel d in duelSource)
{
duels.Add(d.GetUserSet(), d);
}
And finding a duel:
HashSet<string> key = new HashSet<string>();
key.Add("User1");
key.Add("User2");
Duel myDuel = duels[key];
You could try making a custom data type for the key:
class DualKey<T> : IEquatable<DualKey<T>> where T : IEquatable<T>
{
public T Key0 { get; set; }
public T Key1 { get; set; }
public DualKey(T key0, T key1)
{
Key0 = key0;
Key1 = key1;
}
public override int GetHashCode()
{
return Key0.GetHashCode() ^ Key1.GetHashCode();
}
public bool Equals(DualKey<T> obj)
{
return (this.Key0.Equals(obj.Key0) && this.Key1.Equals(obj.Key1))
|| (this.Key0.Equals(obj.Key1) && this.Key0.Equals(obj.Key0));
}
}
Then use a Dictionary<DualKey<string>, string>;
Something quick.
class UserScores {
public string Key { get; set; }
public int User1Score { get; set; }
public int User2Score { get; set; }
public UserScores(string username1, string username2)
{
Key = username1 + ":" + username2;
}
}
void Main()
{
var userScore = new UserScores("fooUser", "barUser");
var scores = new Dictionary<string, UserScores>();
scores.Add(userScore.Key, userScore);
// Or use a list
var list = new List<UserScores>();
list.Add(userScore);
list.Single (l => l.Key == userScore.Key);
}
Although a proper solution in my opinion would use a better thought out UserScores object that tracks that particular "duel" session.
Since a single person can be involved in at most one duel at a time, you can use a single dictionary to directly "index" both endpoints in all duels, something like this:
class Duel {
public Duel(string user1, string user2) {
Debug.Assert(user1 != user2);
User1 = user1;
User2 = user2;
}
public readonly string User1;
public readonly string User2;
public int User1Score;
public int User2Score;
}
class Program {
static void Main(string[] args) {
var dict = new Dictionary<string, Duel>();
// Add a new duel. A single duel has two keys in the dictionary, one for each "endpoint".
var duel = new Duel("Jon", "Rob");
dict.Add(duel.User1, duel);
dict.Add(duel.User2, duel);
// Find Jon's score, without knowing in advance whether Jon is User1 or User2:
var jons_duel = dict["Jon"];
if (jons_duel.User1 == "Jon") {
// Use jons_duel.User1Score.
}
else {
// Use jons_duel.User2Score.
}
// You can just as easily find Rob's score:
var robs_duel = dict["Rob"];
if (robs_duel.User1 == "Rob") {
// Use robs_duel.User1Score.
}
else {
// Use robs_duel.User2Score.
}
// You are unsure whether Nick is currently duelling:
if (dict.ContainsKey("Nick")) {
// Yup!
}
else {
// Nope.
}
// If Jon tries to engage in another duel while still duelling Rob:
var duel2 = new Duel("Jon", "Nick");
dict.Add(duel2.User1, duel); // Exception! Jon cannot be engaged in more than 1 duel at a time.
dict.Add(duel2.User2, duel); // NOTE: If exception happens here instead of above, don't forget remove User1 from the dictionary.
// Removing the duel requires removing both endpoints from the dictionary:
dict.Remove(jons_duel.User1);
dict.Remove(jons_duel.User2);
// Etc...
}
}
This is just a basic idea, you might consider wrapping this functionality in your own class...
This is all in C#, using .NET 2.0.
I have two lists of objects. They are not related objects, but they do have certain things in common that can be compared, such as a GUID-based unique identifier. These two lists need to be filtered by another list which just contains GUIDs which may or may not match up with the IDs contained in the first two lists.
I have thought about the idea of casting each object list to just object and sorting by that, but I'm not sure that I'll be able to access the ID property once it's cast, and I'm thinking that the method to sort the two lists should be somewhat dumb in knowing what the list to be sorted is.
What would be the best way to bring in each object list so that it can be sorted against the list with only the IDs?
You should make each of your different objects implement a common interface. Then create an IComparer<T> for that interface and use it in your sort.
Okay, if you have access to modify your original classes only to add the interface there, Matthew had it spot on. I went a little crazy here and defined out a full solution using 2.0 anonymous delegates. (I think I'm way addicted to 3.0 Lambda; otherwise, I probably would've written this out in foreach loops if I was using 2005 still).
Basically, create an interface with the common properties. Make yoru two classes implement the interface. Create a common list casted as the interface, cast and rip the values into the new list; remove any unmatched items.
//Program Output:
List1:
206aa77c-8259-428b-a4a0-0e005d8b016c
64f71cc9-596d-4cb8-9eb3-35da3b96f583
List2:
10382452-a7fe-4307-ae4c-41580dc69146
97f3f3f6-6e64-4109-9737-cb72280bc112
64f71cc9-596d-4cb8-9eb3-35da3b96f583
Matches:
64f71cc9-596d-4cb8-9eb3-35da3b96f583
Press any key to continue . . .
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
//test initialization
List<ClassTypeA> list1 = new List<ClassTypeA>();
List<ClassTypeB> list2 = new List<ClassTypeB>();
ClassTypeA citem = new ClassTypeA();
ClassTypeB citem2 = new ClassTypeB();
citem2.ID = citem.ID;
list1.Add(new ClassTypeA());
list1.Add(citem);
list2.Add(new ClassTypeB());
list2.Add(new ClassTypeB());
list2.Add(citem2);
//new common list.
List<ICommonTypeMakeUpYourOwnName> common_list =
new List<ICommonTypeMakeUpYourOwnName>();
//in english, give me everything in list 1
//and cast it to the interface
common_list.AddRange(
list1.ConvertAll<ICommonTypeMakeUpYourOwnName>(delegate(
ClassTypeA x) { return (ICommonTypeMakeUpYourOwnName)x; }));
//in english, give me all the items in the
//common list that don't exist in list2 and remove them.
common_list.RemoveAll(delegate(ICommonTypeMakeUpYourOwnName x)
{ return list2.Find(delegate(ClassTypeB y)
{return y.ID == x.ID;}) == null; });
//show list1
Console.WriteLine("List1:");
foreach (ClassTypeA item in list1)
{
Console.WriteLine(item.ID);
}
//show list2
Console.WriteLine("\nList2:");
foreach (ClassTypeB item in list2)
{
Console.WriteLine(item.ID);
}
//show the common items
Console.WriteLine("\nMatches:");
foreach (ICommonTypeMakeUpYourOwnName item in common_list)
{
Console.WriteLine(item.ID);
}
}
}
interface ICommonTypeMakeUpYourOwnName
{
Guid ID { get; set; }
}
class ClassTypeA : ICommonTypeMakeUpYourOwnName
{
Guid _ID;
public Guid ID {get { return _ID; } set { _ID = value;}}
int _Stuff1;
public int Stuff1 {get { return _Stuff1; } set { _Stuff1 = value;}}
string _Stuff2;
public string Stuff2 {get { return _Stuff2; } set { _Stuff2 = value;}}
public ClassTypeA()
{
this.ID = Guid.NewGuid();
}
}
class ClassTypeB : ICommonTypeMakeUpYourOwnName
{
Guid _ID;
public Guid ID {get { return _ID; } set { _ID = value;}}
int _Stuff3;
public int Stuff3 {get { return _Stuff3; } set { _Stuff3 = value;}}
string _Stuff4;
public string Stuff4 {get { return _Stuff4; } set { _Stuff4 = value;}}
public ClassTypeB()
{
this.ID = Guid.NewGuid();
}
}
}
Using only .NET 2.0 methods:
class Foo
{
public Guid Guid { get; }
}
List<Foo> GetFooSubset(List<Foo> foos, List<Guid> guids)
{
return foos.FindAll(foo => guids.Contains(foo.Guid));
}
If your classes don't implement a common interface, you'll have to implement GetFooSubset for each type individually.
I'm not sure that I fully understand what you want, but you can use linq to select out the matching items from the lists as well as sorting them. Here is a simple example where the values from one list are filtered on another and sorted.
List<int> itemList = new List<int>() { 9,6,3,4,5,2,7,8,1 };
List<int> filterList = new List<int>() { 2, 6, 9 };
IEnumerable<int> filtered = itemList.SelectMany(item => filterList.Where(filter => filter == item)).OrderBy(p => p);
I haven't had a chance to use AutoMapper yet, but from what you describe you wish to check it out. From Jimmy Bogard's post:
AutoMapper conventions
Since AutoMapper flattens, it will
look for:
Matching property names
Nested property names (Product.Name
maps to ProductName, by assuming a
PascalCase naming convention)
Methods starting with the word “Get”,
so GetTotal() maps to Total
Any existing type map already
configured
Basically, if you removed all the
“dots” and “Gets”, AutoMapper will
match property names. Right now,
AutoMapper does not fail on mismatched
types, but for some other reasons.
I am not totally sure what you want as your end results, however....
If you are comparing the properties on two different types you could project the property names and corresponding values into two dictionaries. And with that information do some sort of sorting/difference of the property values.
Guid newGuid = Guid.NewGuid();
var classA = new ClassA{Id = newGuid};
var classB = new ClassB{Id = newGuid};
PropertyInfo[] classAProperties = classA.GetType().GetProperties();
Dictionary<string, object> classAPropertyValue = classAProperties.ToDictionary(pName => pName.Name,
pValue =>
pValue.GetValue(classA, null));
PropertyInfo[] classBProperties = classB.GetType().GetProperties();
Dictionary<string, object> classBPropetyValue = classBProperties.ToDictionary(pName => pName.Name,
pValue =>
pValue.GetValue(classB, null));
internal class ClassB
{
public Guid Id { get; set; }
}
internal class ClassA
{
public Guid Id { get; set; }
}
classAPropertyValue
Count = 1
[0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
classBPropetyValue
Count = 1
[0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
Thist should essentially get you what you want - but you may be better of using linq
class T1
{
public T1(Guid g, string n) { Guid = g; MyName = n; }
public Guid Guid { get; set; }
public string MyName { get; set; }
}
class T2
{
public T2(Guid g, string n) { ID = g; Name = n; }
public Guid ID { get; set; }
public string Name { get; set; }
}
class Test
{
public void Run()
{
Guid G1 = Guid.NewGuid();
Guid G2 = Guid.NewGuid();
Guid G3 = Guid.NewGuid();
List<T1> t1s = new List<T1>() {
new T1(G1, "one"),
new T1(G2, "two"),
new T1(G3, "three")
};
List<Guid> filter = new List<Guid>() { G2, G3};
List<T1> filteredValues1 = t1s.FindAll(delegate(T1 item)
{
return filter.Contains(item.Guid);
});
List<T1> filteredValues2 = t1s.FindAll(o1 => filter.Contains(o1.Guid));
}
}
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...
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.
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]; }
}
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.
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.
Could you use an sqlite database as the backing? With sqlite you even have the option of building an in-memory db.