Simple lookup function - c#

I've got a simple struct which I want to use as a lookup table:
public struct TileTypeSize
{
public string type;
public Size size;
public TileTypeSize(string typeIn, Size sizeIn)
{
type = typeIn;
size = sizeIn;
}
}
I populate this thusly:
tileTypeSizeList.Add(new TileTypeSize("W",rectangleSizeWall));
tileTypeSizeList.Add(new TileTypeSize("p",rectangleSizePill));
tileTypeSizeList.Add(new TileTypeSize("P",rectangleSizePowerPill));
tileTypeSizeList.Add(new TileTypeSize("_",rectangleSizeWall));
tileTypeSizeList.Add(new TileTypeSize("=",rectangleSizeWall));
What is the most efficient way to look up the size for a given type?
Thanks in advance!

In general, the most efficient way would be to put your data into a Dictionary or similar container instead (SortedDictionary and SortedList have small differences from Dictionary and are an even better fit in certain cases):
var dict = new Dictionary<string, Size>
{
{ "W", rectangleSizeWall },
// etc
}
And then:
var size = dict["W"];
You can of course still iterate sequentially over the values in the dictionary if there is reason to do so.
If 5 types is all you are going to be looking up (i.e. the size of the problem is ridiculously small) then a straight list like you have would likely be faster than an associative container. So:
var tileStruct = tileTypeSizeList.FirstOrDefault(s => s.type == "W");
if (tileStruct.type == "") {
// not found
}
else {
var size = tileStruct.size;
}
You may remove the "if found" check if you are sure that you will never have a search miss.

If you know there will be one and only one match in the collection, then you can use:
var size = tileTypeSizeList.Single(t => t.type == someType).size;
If not, you'll have to be a little more clever to properly handle the cases where no match is found:
Size size;
var match =
tileTypeSizeList
.Cast<TileTypeSize?>().FirstOrDefault(t => t.type == someType);
if(match != null) size = match.size;
Keep in mind, though, that there are better ways to store this information if that is the only data in the struct. I would suggest a Dictionary<string, Size>.

var type = tileTypeSizeList.FirstOrDefault(t => t.type == someType);
if(type==null) throw new NotFoundException();
return type.size;
But if the list is big and you need to lookup data really often you better use Dictionary as noticed in other answers.

Use a Dictionary instead of a List:
Dictionary<string, TileTypeSize> tileTypeSizeDictionary = Dictionary<string, TileTypeSize>();
tileTypeSizeDictionary.Add("W", new TileTypeSize("W",rectangleSizeWall));
...
You lookup your elements with:
TileTypeSize rectangleSizeWall = tileTypeSizeDictionary["W"];
A dictionary is faster than a list when you need to lookup by key.

Related

Using lambdas to filter using a multidimensional array or map of filter parameters?

Given that I have a collection of objects and a collection of key=value pairs, how would I construct a lambda (or, if lambda is inappropriate, another expression) so that it filters the original collection based on the settings of the second.
Example: a collection of missions and given a map contains type=flight and location=USA, return all flight missions in the USA.
This question is in conjunction with another question, but as they are separate things I made two questions out of it.
Assume I have something like:
Dictionary<string, string> filters; //type and location are keys, flight and USA are the values, respectively
List<Mission> missions;
Existing currently we have something like:
var badIds;
return missions.Where(x => !badIds.contains(x.ids)))
.Select(x => x.ids);
The thing is, in this case I have to know at compile time that I want to filter based on ids and use a single dimension array like collection to do it. I want to use two dimensions and include not only what values I'm looking for, but what parameter to look in for it while also dynamically expanding the number of filter operations based on the number of parameters. They may specify one, the other, both, more parameters, less, in any combination, etc (0 parameters may be a special case, but that's easy enough).
I want something like:
for ( KeyValuePair kvp in filters )
{
missions = missions.Where(x => x.(kvp.Key) == kvp.Value);
}
//I'm new to lambdas and C# so this is probably terrible.
From your description, this is what I gather you are trying to do:
IEnumerable<Mission> FindMissions(IEnumerable<Mission> missions, string flight, string location)
{
var results = new List<Mission>();
foreach (Mission m in missions)
{
if (m.Type == flight && m.Location == location)
{
results.Add(m);
}
}
return results;
}
I'm posting my own solution to this so it can be judged. Let me know what you guys think.
for ( KeyValuePair kvp in filters )
{
missions = missions.Where(x => x.GetType().GetProperty(kvp.Key).GetValue(x, null) == kvp.Value;
}
I've never done reflection before so the performance implications are unknown.

Is there a more efficient way of creating a list based on an existing list and a lookup list?

I have the following method that takes an extremely long time to run and would love some help to make it run faster and or be more efficient.
The main responsibility of the method is to take a list of data points created from a CSV file, map the Name property of the file datapoints to the to the HistorianTagname property in a list of tagnames by the DataLoggerTagname property and create a resulting list from the mapping. If the mapping does not exist, the file datapoint is ignored.
I know it that was long-winded, but I hope it makes sense. It may be easier just to look at the method:
private IEnumerable<DataPoint> GetHistorianDatapoints(IEnumerable<DataPoint> fileDatapoints, IEnumerable<Tagname> historianTagnames)
{
/**
** REFACTOR THIS
**/
foreach (var fileDatapoint in fileDatapoints)
{
var historianTagname = historianTagnames.FirstOrDefault(x => x.DataLoggerTagname.Equals(fileDatapoint.Name, StringComparison.OrdinalIgnoreCase));
if (historianTagname != null)
{
var historianDatapoint = new DataPoint();
historianDatapoint.Name = historianTagname.HistorianTagname;
historianDatapoint.Date = fileDatapoint.Date;
historianDatapoint.Value = fileDatapoint.Value;
yield return historianDatapoint;
}
}
}
Notes:
I have complete control of classes and methods of mapping, so if I am doing something fundamentally wrong. I would love to know!
Thanks!
I would start by fixing up:
var historianTagname = historianTagnames.FirstOrDefault(x => x.DataLoggerTagname.Equals(fileDatapoint.Name, StringComparison.OrdinalIgnoreCase))
That's a pretty expensive operation to run every iteration through this loop.
Below is my proposition:
private IEnumerable<DataPoint> GetHistorianDatapoints(IEnumerable<DataPoint> fileDatapoints, IEnumerable<Tagname> historianTagnames)
{
var tagNameDictionary = historianTagnames.ToDictionary(t => t.DataLoggerTagname, StringComparer.OrdinalIgnoreCase);
foreach (var fileDatapoint in fileDatapoints)
{
if (tagNameDictionary.ContainsKey(fileDatapoint.Name))
{
var historianTagname = tagNameDictionary[fileDatapoint.Name];
var historianDatapoint = new DataPoint();
historianDatapoint.Name = historianTagname.HistorianTagname;
historianDatapoint.Date = fileDatapoint.Date;
historianDatapoint.Value = fileDatapoint.Value;
yield return historianDatapoint;
}
}
}
Like #Sheldon Warkentin said FirstOrDefault is probably bottle neck of your function, i s better to create historianTagnames a Dictionary where Name is key, then in your function you can get value by key.
Something like bellow:
// this is passed to method
IDictionary<string, Tagname> historianTagnames;
// .. method body
var historianTagname = historianTagnames[fileDatapoint.Name];
ofcourse you need to add proper if's.
As others have said, a Dictionary<string, Tagname> might perform better.
var historianDict = new Dictionary<string, Tagname>();
foreach (var tagName in historianTagnames) {
historianDict[tagName.DataLoggerTagname.ToLowerInvariant()] = tagName;
}
foreach (var fileDatapoint in fileDatapoints) {
if (historianDict.ContainsKey(fileDatapoint.Name.ToLowerInvariant()) {
// ...
}
}

Intersecting number of keyValuePair Lists according to the Key element

I have number of lists of type:
public List<KeyValuePair<KeyValuePair<string, string>,
List<KeyValuePair<string, string>>>> rawComparisonObject;
I want to get the intersection of these lists according to the "key" of the KeyValuePair that constructs the List
I tried:
List2 = list1.Intersect(list2).Intersect(list3)...... etc , but as you can see it Intersects all the KeyValuePair variable, not the one I want.
I also tried
Intersect lists on KeyValuePair key?
In the following form:
public List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> getCommon(List<ResourceInformation> input)
{
List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> rawComparisonObject =
new List<List<KeyValuePair<KeyValuePair<string,string>,List<KeyValuePair<string,string>>>>>();
foreach (ResourceInformation item in input)
{
rawComparisonObject.Add(item.rawComparisonObject);
}
foreach (List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>> item in rawComparisonObject)
{
}
List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> common =
new List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>>();
for (int i = 0; i < (rawComparisonObject.Count-1); i++)
{
var keysFromB = new HashSet<KeyValuePair<string, string>>(rawComparisonObject[i].Select(x => x.Key));
var result = rawComparisonObject[i+1].Where(x => keysFromB.Remove(x.Key));
common.Add(result.ToList());
}
return common;
}
it returned very faulty values,
is there
Any easy way to do this ?
I use this data structure in linked data work, to get common objects as a result of comparing between objects
Ex: Batman vs. Inception
should return:
Type : Movie | Movie
Starring : Christian Bale | Leonardo Dicaprio
of course everything is highlighted with it's URI link, that's why I need keyValuePair one for URI and other for label....
I tried my best to explain this complex data-structure. hope it's clear enough
As I understand the code you've written, here's my (revised) translation:
public List<List<KeyValuePair<KeyValuePair<string, string>, List<KeyValuePair<string, string>>>>> getCommon(List<ResourceInformation> input)
{
var rawComparisonObject =
input.Select(item => item.rawComparisonObject).ToList();
var common = rawComparisonObject.Zip(
rawComparisonObject.Skip(1),
(prevItems, nextItems) =>
(from next in nextItems
join prev in prevItems on next.Key equals prev.Key
select next).ToList()).ToList();
return common;
}
Edit: the above translation, omits the empty foreach loop in the middle and uses the join as a filter, projecting only the 'next' elements that pass the join criteria. I tend to favor join for this kind of filtering as I know it utilizes hashing under the covers to efficiently perform the matching it does.
The problem with my prior version was that it collected the join results using a 'group join' variable which led to that extra enumerable that we didn't want. After the change, the inner ToList() is analogous to the result variable in the original code sample provided in the post. The outer ToList() is the final common variable's (re)packaging of the results. I believe this will provide results similar to that of the original code; however, I strongly encourage testing to verify that the results meet expectations.
IMHO, the right thing to do would be to refactor to simplify the generics use until we can reason about them better. In a brief interim attempt I changed GetCommon to a generic type like this (later changing it back):
public List<List<KeyValuePair<T, List<T>>>> GetCommon<T>(/*List<ResourceInformation> input*/)
From there, we could promote the rawComparisonObject list to a parameter of the method - and in the process of doing so, we would replace the current parameter of the method. The use of var typing allows us to avoid changing the type for the common local variable (so long as we're careful that the output type matches the expected return type, which was my bad on the original translation.)
There are many more design ideas and questions than I could comfortably examine here, so I am going to close without attempting to do so. I do want to offer that this was a good challenge - sometimes LINQ isn't the right choice, but even when it isn't the right choice, a change of approach can make it worth trying. Thanks!
you can do this with linq, although likely you should change your data model to be more efficient:
var keys = list1.select( kv => kv.Key).intersection(list2.select(kv => kv.Key)
var result = list1.where( key => keys.contains(key).TolLst()
If you only want to intersect the KeyValuePairs on their key you should implement a custom IEqualityComparer<T> and use Intersect() method like this:
class KeyValyePairComparer : IEqualityComparer<KeyValuePair<string, string>>
{
public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
{
return x.Key == y.Key;
}
public int GetHashCode(KeyValuePair<string, string> item)
{
return item.Key.GetHashCode();
}
}
Using the implementation above you can get the intersection with the query:
var comparer = new KeyValuePairComparer();
var intersection = list1.Intersect(list2, comparer).Intersect(list3, comparer);

how to add an associative index to an array. c#

i have an array of custom objects. i'd like to be able to reference this array by a particular data member, for instance myArrary["Item1"]
"Item1" is actually the value stored in the Name property of this custom type and I can write a predicate to mark the appropriate array item. However I am unclear as to how to let the array know i'd like to use this predicate to find the array item.
I'd like to just use a dictionary or hashtable or NameValuePair for this array, and get around this whole problem but it's generated and it must remain as CustomObj[]. i'm also trying to avoid loading a dictionary from this array as it's going to happen many times and there could be many objects in it.
For clarification
myArray[5] = new CustomObj() // easy!
myArray["ItemName"] = new CustomObj(); // how to do this?
Can the above be done? I'm really just looking for something similar to how DataRow.Columns["MyColumnName"] works
Thanks for the advice.
What you really want is an OrderedDictionary. The version that .NET provides in System.Collections.Specialized is not generic - however there is a generic version on CodeProject that you could use. Internally, this is really just a hashtable married to a list ... but it is exposed in a uniform manner.
If you really want to avoid using a dictionary - you're going to have to live with O(n) lookup performance for an item by key. In that case, stick with an array or list and just use the LINQ Where() method to lookup a value. You can use either First() or Single() depending on whether duplicate entries are expected.
var myArrayOfCustom = ...
var item = myArrayOfCustom.Where( x => x.Name = "yourSearchValue" ).First();
It's easy enough to wrap this functionality into a class so that external consumers are not burdened by this knowledge, and can use simple indexers to access the data. You could then add features like memoization if you expect the same values are going to be accessed frequently. In this way you could amortize the cost of building the underlying lookup dictionary over multiple accesses.
If you do not want to use "Dictionary", then you should create class "myArrary" with data mass storage functionality and add indexers of type "int" for index access and of type "string" for associative access.
public CustomObj this [string index]
{
get
{
return data[searchIdxByName(index)];
}
set
{
data[searchIdxByName(index)] = value;
}
}
First link in google for indexers is: http://www.csharphelp.com/2006/04/c-indexers/
you could use a dictionary for this, although it might not be the best solution in the world this is the first i came up with.
Dictionary<string, int> d = new Dictionary<string, int>();
d.Add("cat", 2);
d.Add("dog", 1);
d.Add("llama", 0);
d.Add("iguana", -1);
the ints could be objects, what you like :)
http://dotnetperls.com/dictionary-keys
Perhaps OrderedDictionary is what you're looking for.
you can use HashTable ;
System.Collections.Hashtable o_Hash_Table = new Hashtable();
o_Hash_Table.Add("Key", "Value");
There is a class in the System.Collections namespace called Dictionary<K,V> that you should use.
var d = new Dictionary<string, MyObj>();
MyObj o = d["a string variable"];
Another way would be to code two methods/a property:
public MyObj this[string index]
{
get
{
foreach (var o in My_Enumerable)
{
if (o.Name == index)
{
return o;
}
}
}
set
{
foreach (var o in My_Enumerable)
{
if (o.Name == index)
{
var i = My_Enumerable.IndexOf(0);
My_Enumerable.Remove(0);
My_Enumerable.Add(value);
}
}
}
}
I hope it helps!
It depends on the collection, some collections allow accessing by name and some don't. Accessing with strings is only meaningful when the collection has data stored, the column collection identifies columns by their name, thus allowing you to select a column by its name. In a normal array this would not work because items are only identified by their index number.
My best recommendation, if you can't change it to use a dictionary, is to either use a Linq expression:
var item1 = myArray.Where(x => x.Name == "Item1").FirstOrDefault();
or, make an extension method that uses a linq expression:
public static class CustomObjExtensions
{
public static CustomObj Get(this CustomObj[] Array, string Name)
{
Array.Where(x => x.Name == Name).FirstOrDefault();
}
}
then in your app:
var item2 = myArray.Get("Item2");
Note however that performance wouldn't be as good as using a dictionary, since behind the scenes .NET will just loop through the list until it finds a match, so if your list isn't going to change frequently, then you could just make a Dictionary instead.
I have two ideas:
1) I'm not sure you're aware but you can copy dictionary objects to an array like so:
Dictionary dict = new Dictionary();
dict.Add("tesT",40);
int[] myints = new int[dict.Count];
dict.Values.CopyTo(myints, 0);
This might allow you to use a Dictionary for everything while still keeping the output as an array.
2) You could also actually create a DataTable programmatically if that's the exact functionality you want:
DataTable dt = new DataTable();
DataColumn dc1 = new DataColumn("ID", typeof(int));
DataColumn dc2 = new DataColumn("Name", typeof(string));
dt.Columns.Add(dc1);
dt.Columns.Add(dc2);
DataRow row = dt.NewRow();
row["ID"] = 100;
row["Name"] = "Test";
dt.Rows.Add(row);
You could also create this outside of the method so you don't have to make the table over again every time.

Search list of objects based on object variable

I have a list of objects. These objects have three variables, ID, Name, & value. There can be a lot of objects in this list, and I need to find one based on the ID or Name, and change the value.
Example
class objec
{
public string Name;
public int UID;
public string value;
}
List<objec> TextPool = new List<objec>();
How would I find the one entry in TextPool that had the Name of 'test' and change its value to 'Value'.
The real program has many more search options, and values that need changing, so I couldn't just use a Dictionary (though Name and UID or unique identifiers).
Any help would be great
You could use LINQ to find it, then change the element directly:
var item = TextPool.FirstOrDefault(o => o.Name == "test");
if (item != null)
item.value = "Value";
If you wanted to change all elements that match, you could, potentially, even do:
TextPool.Where(o => o.Name == "test").ToList().ForEach(o => o.value = "Value");
However, I personally would rather split it up, as I feel the second option is less maintainable (doing operations which cause side effects directly on the query result "smells" to me)...
var find = TextPool.FirstOrDefault(x => x.Name == "test");
if (find != null)
{
find.Name = "Value";
}
Sounds like a job for LINQ!
var matchedObject =
from t in TextPool
where t.UName == "test"
select t;
This is assuming your search is defined in code. If your code is driven by the UI, you may simply need to do a linear iteration. To search all possible attributes, without indexing, it isn't going to get any faster.
[ Edit: Was beaten to the punch, but leaving this up as an example of a different syntax, plus a link ]
List<objec> TextPool = new List<objec>();
objec found = TextPool.FirstOrDefault(item => item.Name == "test");
if (found != null) found.value = "Value";
If you are going to perform many lookups, you could cache the results in multiple Dictionary<> instances (or Lookup<> instance if keys are not unique).

Categories

Resources