I have some data stored in a dictionary where the values are basically a list of objects with few attributes in them. Right now I'm looping through as following to get the data stored in a specific attribute. These data are then added into a drop down list (unity UI dropdown)
foreach (KeyValuePair<string, List<NameIDValuePair>> kvp in TeamValuePair)
{
List<NameIDValuePair> list = kvp.Value;
if(kvp.Key == teamNames.options[teamNames.value].text)
{
foreach (var rec in list)
{
screenNamesDropDown.options.Add(new TMP_Dropdown.OptionData { text = rec.ScreenName });
}
}
}
teamNames and screenNamesDropDown are dropdown elements part of my unity UI.
The structure of the NameIdValuePair looks as follows:
public class NameIdValuePair
{
public string ScreenName { get; private set; }
public string ScreenId { get; private set; }
}
I would like to optimize this piece of code in a better way using linq - so that it's a bit more readable. Since I'm pretty new to linq, i'm not really sure if I'm using the right keywords when searching for suggestions but so far I haven't had much success in finding any helpful suggestion.
Thanks
As mentioned before instead of looping a Dictionary - where we already know that the keys are unique - you could simply use Dictionary.TryGetValue
// do this only once!
var key = teamNames.options[teamNames.value].text;
if (TeamValuePair.TryGetValue(key, out var list))
{
foreach(var item in list)
{
screenNamesDropDown.options.Add(new TMP_Dropdown.OptionData(item.ScreenName));
}
}
and then actually the only place where you could use Linq if you really want to would maybe be in
var key = teamNames.options[teamNames.value].text;
if (TeamValuePair.TryGetValue(key, out var list))
{
screenNamesDropDown.options.AddRange(list.Select(item => new TMP_Dropdown.OptionData(item.ScreenName)));
}
if this makes it better to read is questionable though.
And in general the question would also be if you always want to Add (AddRange) to the screenNamesDropDown.options or if you maybe want to actually replace the options. Then instead of AddRange you could do
screenNamesDropDown.options = list.Select(item => new TMP_Dropdown.OptionData(item.ScreenName)).ToList();
What I am trying to achieve in C# without using LINQ is to subset a list of custom-class objects based on the value of one of the parameters of these objects.
Let's say the declaration of my list is the following:
List<MyCustom> listofobj = new List<MyCustom>();
Also, assume that the custom-class object "MyCustom" can return two parameters: MyCustom.name and MyCustom.age
Is there a way trough which I can retrieve and save into a temporary new list (let's call it "templist") the subset of the original list (i.e. "listofobj") formed by all its MyCustom objects that have "age" parameter greater than 30? Thanks!
Sure. You can reinvent LINQ:
IEnumerable<MyCustom> WhereByAge(IEnumerable<MyCustom> source, int age)
{
foreach (MyCustom myCustom in source)
{
if (myCustom.Age > age)
{
yield return myCustom;
}
}
}
then:
List<MyCustom> filteredList = new List(WhereByAge(listofobj, 30));
But why? I strongly recommend that if you want LINQ-like behavior, just use LINQ.
This is how you get a subset of original list based on parameters without linq.
List<MyCustom> listofobj = new List<MyCustom>();
List<MyCustom> templist = new List<MyCustom>();
foreach(var obj in listofobj)
{
if(obj.Age > 30)
{
templist.Add(obj);
}
}
You can also use this Linq.
templist = listofobj.Where(obj => obj.Age > 30).ToList();
Linq is slower than a normal code in general. but this should not be problem if this is not hot path in your code. performance difference is negligible.
What would be the best way to make a System.Collections.Generic.List that should only be accessed by an enum? To help describe what I'm looking for, this how I'm doing it right now:
enum MyEnum
{
First,
Second,
Count
}
class MyClass
{
List<SomeClass> myList = new List<SomeClass>((int)MyEnum.Count);
MyClass()
{
myList[(int)MyEnum.First] = new SomeClass(1);
myList[(int)MyEnum.Second] = new SomeClass(2);
}
}
I feel like I'm missing something. Is there a better way of doing this? I'm sorry if the answer is obvious, I found it very difficult to search for something that sounded so generic.
As some background, I would be using this list to access different textures in my code, e.g. the enum would have texture names and each texture would be placed in and later accessed from the List using that texture name.
Would it be possible to use Dictionary instead of List?
var myDict = new Dictionary<MyEnum, SomeClass>();
...
myDict[MyEnum.First] = new SomeClass(1);
myDict[MyEnum.Second] = new SomeClass(2);
Or, as #JimHurley suggested (see http://msdn.microsoft.com/en-us/library/bb531208.aspx) just
var myDict = new Dictionary<MyEnum, SomeClass>()
{
{ MyEnum.First, new SomeClass(1)},
{ MyEnum.Second, new SomeClass(2)}
};
If you want to add a new element to List<T> you have to use it's Add method.Indexer can be used only to modify existing items. I think in this case using a Dictionary<MyEnum, SomeClass> would be more appropriate.
I have an object that contains a list of child objects, each of which in turn contains a list of children, and so on. Using that first generation of children only, I want to combine all those lists as cleanly and cheaply as possible. I know I can do something like
public List<T> UnifiedListOfTChildren<T>()
{
List<T> newlist = new List<T>();
foreach (childThing in myChildren)
{
newlist = newlist.Concat<T>(childThing.TChildren);
}
return newlist;
}
but is there a more elegant, less expensive LINQ method I'm missing?
EDIT If you've landed at this question the same way I did and are new to SelectMany, I strongly recommend this visual explanation of how to use it. Comes up near the top in google results currently, but is worth skipping straight to.
var newList = myChildren.SelectMany(c => c.TChildren);
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.