How can we create a list of objects in C# and acces them by a specific field inside this object?
For example take this object:
class Section
{
public string Name { get; }
public long Size { get; }
public Section(string name, long size)
{
Name = name;
Size = size;
}
}
I would like to create a list of these objects which I can access by Section.Name.
I can create a dictionary like:
private static readonly Dictionary<string, Section> validSections = new Dictionary<string, Section>
{
{ "section-a", new Section("section-a", 1) },
{ "section-b", new Section("section-b", 2) },
{ "section-c", new Section("section-c", 3) },
{ "section-d", new Section("section-d", 4) },
};
But as you see, I have to declare the section name twice, which looks inelegant. Is there a more elegant way?
But as you see, I have to declare the section name twice, which looks
inelegant. Is there a more elegant way?
To avoid repetitve typing you can create dictionary from collection of sections via ToDictionary call:
private static readonly Dictionary<string, Section> validSections = new[] {
new Section("section-a", 1),
new Section("section-b", 2),
new Section("section-c", 3),
new Section("section-d", 4)
}.ToDictionary(s => s.Name);
If this is not time critical then you can use List<Section> list = new ArrayList<Section>(); and store data in it.
Later you can use LINQ to query based on name .where(x=>x.Name=="somename")
First of all your Model class can look like:
class Section
{
public string Name { get; set; }
public long Size { get; set; }
}
You don't need the Name twice so you can just create a list:
private static List<Section> myList = new List<Section>();
myList.add(new Section {Name = "section-a", Size = 1});
// do this for all the sections ...
Then as other answers suggest you can use LINQ:
myList.Single(s => s.Name == "section-a");
Will simply return the single element where the name is "section-a".
Read more about LINQ here: https://msdn.microsoft.com/en-us/library/bb308959.aspx
You could write a function that takes a list of sections and returns the corresponding dictionary. Something like:
public static Dictionary<string, Section> SectionDictionary(List<Section> sections) {
var dict = new Dictionary<string, Section>();
foreach (var section in sections)
dict.Add(section.Name, section);
return dict;
}
You can just access the elements using LINQ:
var list = ...;
var el = list.FirstOrDefault(o => o.Name = nameValue);
Or you can create a (collection) class that implements your own indexer / getter logic. E.g. (pseudocode)
public class MyCollection : Collection<Section>
{
public Section this[string nameValue]
{
get
{
return this.FirstOrDefault(o => o.Name == nameValue);
}
}
}
Then the usage is:
var coll = new MyCollection() ....;
var el = coll["Some name"];
Related
I want to create "list of list of list". It should be:
Group (has a list of Members)
Member (has a Name and list of Properties)
Property (has Name and Value)
What I want is to have a possibility to add Property into Member (specified by its name) inside defined Group. Someting like this:
membersgroup.AddNewMember(memberXYZ);
...
membersgroup.memberXYZ.AddProperty(nameXYZ, valueXYZ).
I have trouble achieving this using list... I found class Hashable, but I am not sure if this is usable... and cannot make it works too...
Thank for any suggestion :)
Well, I suggest you create a custom class instead of your approach. But otherwise you can use a Dictionary.
var properties = new Dictionary<string, string>();
properties.Add("Prop1", "Value");
var members = new Dictionary<string, Dictionary<string, string>>();
members.Add("Member1", properties);
var group = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
group.Add("GroupName", members);
public class Group
{
public Group()
{
Members = new List<Member>();
}
public IEnumerable<Member> Members { get; set; }
}
public class Member
{
public Member()
{
Properties = new Dictionary<string, string>();
}
public string Name { get; set; }
IDictionary<string, string> Properties { get; set; }
}
The dictionary can take a key and a value, and the key should be unique.
You can also create a class property if you want to add another thing beside the name and the value
I would use indexers.
Here's a partial implementation:
class Group
{
private List<Member> _members;
public string this
{
get
{
return _members.Find(m => m.Name == value);
}
// You can also implement set here if you want...
}
}
class Member
{
private List<Property> _properties;
public string Name {get;set;}
public string this
{
get
{
return _properties.Find(m => m.Name == value);
}
}
}
class Property
{
public string Name {get;set;}
public string Value {get;set;}
}
And the usage:
var g = new Group();
g[memberName][propertyName].Value = someValue;
Note: This implementation is partial! it still needs constructor logic and any other logic you might need.
Likely the best solution is to use the C# class Dictionary - as suggested by zetawars, or a custom class - as suggested by Zohar Peled, or some mix of the two - as suggested by gandalf.
However, in order to use syntax similar to what is requested in the question...
membersgroup.AddNewMember(memberXYZ);
...
membersgroup.memberXYZ.AddProperty(nameXYZ, valueXYZ).
You can abuse ExpandoObject and Action, and do something awesome like this:
dynamic membersgroup = new ExpandoObject();
var getNewMemberObject = new Func<dynamic>(() =>
{
dynamic memberObject = new ExpandoObject();
var addPropertyAction = new Action<string, string>((propertyName, propertyValue) =>
{
((IDictionary<string, object>)memberObject).Add(propertyName, propertyValue);
});
memberObject.AddProperty = addPropertyAction;
return memberObject;
});
var addNewMemberAction = new Action<string>((memberName) =>
{
((IDictionary<string, object>)membersgroup).Add(memberName, getNewMemberObject());
});
membersgroup.AddNewMember = addNewMemberAction;
string memberXYZ = nameof(memberXYZ);
string nameXYZ = nameof(nameXYZ);
string valueXYZ = nameof(valueXYZ);
// look we did it!
membersgroup.AddNewMember(memberXYZ);
membersgroup.memberXYZ.AddProperty(nameXYZ, valueXYZ);
// and it actually works
var actualValue = membersgroup.memberXYZ.nameXYZ;
Console.WriteLine(actualValue); // "valueXYZ"
(for science of course)
I am trying to serialize nested objects using reflection. I am able to do this fine for properties containing a single value, but I am having trouble with list type properties that contain another class.
In the code sample below I have a class Dish which contains a list of of Recipe classes as a property, which itself contains a list of Step classes.
I am able to get the PropertyInfo of the List property, but when I try to get the contents of it by invoking the get method, I get a simple object back, not a List of e.g. Steps:
var listObjects = property.GetGetMethod().Invoke(dish, null);
I managed to cast that into a List of objects like this:
List<object> listValues = ( listObjects as IEnumerable<object>).Cast<object>().ToList();
Now at least I can iterate over this List, but I cannot get the acutal properties of the original classes like the step description.
So I know the type of the List via property.PropertyType.GenericTypeArguments.First(), but its at runtime. I am thinking on how to perform a proper cast to transform my List<object> into a conrete type like List<Step>.
What I want to achieve: Serialize all property values of dish and all its attached Lists of objects.
I appreciate any ideas.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var dish = new Dish(){
Recipes = new List<Recipe>(){
new Recipe(){
RecipeName = "Preparation",
Steps = new List<Step>(){
new Step(){
Description = "Prepare Stuff",
}
}
},
new Recipe(){
RecipeName = "Main Course",
Steps = new List<Step>(){
new Step(){
Description = "Do this",
},
new Step(){
Description = "Then do that",
}
}
}, }, };
var serializer = new Serializer();
serializer.SerializeDish(dish);
}
}
public class Serializer
{
public void SerializeDish(Dish dish)
{
var dishType = typeof (Dish);
var listProps = dishType.GetProperties().Where(x => (x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof (List<>)));
foreach (var property in listProps)
{
var propertyGetMethod = property.GetGetMethod();
var listObjects = propertyGetMethod.Invoke(dish, null);
Console.WriteLine("Name:"+property.Name + " " + "List-Type:"+property.PropertyType.GenericTypeArguments.First());
//Here its getting fuzzy
List<object> listValues = ( listObjects as IEnumerable<object>).Cast<object>().ToList();
foreach ( var item in listValues ) {
Console.WriteLine(item);
}
}
}
}
public class Dish
{
public List<Recipe> Recipes {get;set;}
}
public class Recipe
{
public string RecipeName{get;set;}
public List<Step> Steps {get;set;}
}
public class Step
{
public string Description {get;set;}
}
I have a dropdownlistand I would like to bind my Dictionaryto it where the keys are the displayed items and the values are stored in the value attribute tag.
I found this:
bind-html-dropdownlist-with-static-items
But it doesn't allow for an unknown number of items to be bound as you have to manually enter the SelectListItem. I tried this:
#Html.DropDownList("OverrideConfigList", new List<SelectListItem>
{
for(KeyValuePair<string, string> entry in Model.IdentifiFIConfiguration.Config.Configuration)
{
new SelectListItem { Text = entry.Key, Value = entry.Value}
}
})
But that didn't work either. Any suggestions?
Edit:
My model class looks basically like this:
public class DefaultConfigurationModel
{
public IdentifiFIConfiguration IdentifiFIConfiguration { get; set; }
public String FiKeySelection { get; set; }
public List<String> FiConfigKeys
{
get
{
if (IdentifiFIConfiguration.Config == null)
{
return null;
}
List<string> fiConfigKeys = new List<string>();
foreach (KeyValuePair<string, string> entry in IdentifiFIConfiguration.Config.Configuration)
{
fiConfigKeys.Add(entry.Key);
}
return fiConfigKeys;
}
}
}
IdentifiFIConfiguration holds Config which looks like this:
public class IdentifiConfiguration
{
public Dictionary<String, String> Configuration { get; set; }
public static IdentifiConfiguration DeserializeMapFromXML(string xml)
{
Dictionary<string, string> config = new Dictionary<string, string>();
XmlDocument configDoc = new XmlDocument();
configDoc.LoadXml(xml);
foreach (XmlNode node in configDoc.SelectNodes("/xml/*"))
{
config[node.Name] = node.InnerText;
}
IdentifiConfiguration identifiConfiguration = new IdentifiConfiguration()
{
Configuration = config
};
return identifiConfiguration;
}
}
Your attempt is close, but the syntax is wrong. You can't execute a for loop in a list initializer like that.
Essentially, what you're trying to do is transform a collection of one thing (key/value pairs) to a collection of another thing (SelectListItems). You can do that with a LINQ select:
Model.IdentifiFIConfiguration.Config.Configuration.Select(c => new SelectListItem { Text = c.Key, Value = c.Value })
You may optionally need to add a .ToList() or .ToArray() at the end either for static typing or to materialize the collection sooner, but that wouldn't affect the logic of the statement.
This transformation would result in the list of SelectListItems that you want:
#Html.DropDownList(
"OverrideConfigList",
Model.IdentifiFIConfiguration.Config.Configuration.Select(c => new SelectListItem { Text = c.Key, Value = c.Value })
)
you cannot bind a dropdown list to a dictionnary
you need scalar property to bind select value
also you need a collection to bind a dropdownlist
you could do this but that ugly
#Html.DropDownList("SelectedItemValue", new SelectList(MyDictionary, "Key", "Value"))
Is there a List<> similar to a two dimension array? For each entry there is a number and text.
You can use Dictionary<int,String>
Sample:
Dictionary<int,string> samp = new Dictionary<int,string>();
dictionary.Add(1, "text1");
dictionary.Add(2, "text2");
Or, have a custom class which defines your requirement
public class Sample
{
public int Number;
public string Text;
}
Sample:
List<Sample> req = new List<Sample>();
Sample samObj = new Sample();
samObj.Number = 1;
samObj.Text = "FirstText";
req.Add(samObj);
There are many options, I describe some of them for you
use Dictionary<int, string>
Pros: very fast lookup
Cons: you can not have two string with same number, you don't have a List
var list2d = new Dictionary<int, string>();
list2d[1] = "hello";
list2d[2] = "world!";
foreach (var item in list2d)
{
Console.WriteLine(string.Format("{0}: {1}", item.Key, item.Value);
}
use Tuple<int, string>
Pros: very simple and handy tool, you have a List
Cons: Tuples are immutable, you can not change their values once you create them, reduces code readability (Item1, Item2)
var list2d = new List<Tuple<int, string>>();
list2d.Add(new Tuple(1, "hello"));
list2d.Add(Tuple.Create(1, "world");
foreach (var item in list2d)
{
Console.WriteLine(string.Format("{0}: {1}", item.Item1, item.Item2);
}
use a defined class,
Pros: you have a List, very customizable
Cons: you should write more code to setup
public class MyClass
{
public int Number { get; set; }
public string Text { get; set; }
}
var list2d = new List<MyClass>();
list2d.Add(new MyClass() { Number = 1, Text = "hello" });
list2d.Add(new MyClass { Number = 2, Text = "world" });
foreach (var item in list2d)
{
Console.WriteLine(string.Format("{0}: {1}", item.Number, item.Text);
}
Custom class or dictionary are good options, you can also use the Tuple generic...
var i = new List<Tuple<int, string>>();
Dictionary requires that whatever value is used as key must be unique. So not ideal without uniqueness.
A custom class is preferable if you don't mind a little more code and gives you scope to extend later on if you decide you want other data in there.
Tuple is quick and easy but you lose readability and objects can not be edited.
Define a class wih a string and an int property
public class MyClass
{
public string MyStr {get;set;}
public int MyInt {get;set;}
}
then create a list of this class
List<Myclass> myList = new List<MyClass>();
myList.add(new MyClass{MyStr = "this is a string", MyInt=5});
Hope it will help
public class DATA
{
public int number;
public string text;
}
List<DATA> list = new List<DATA>();
How can I store data from 2 columns (from a database) in a List
List<string> _items = new List<string>();
Any help is appreciated
You create a class that will represent a row with 2 columns:
public class Foo
{
// obviously you find meaningful names of the 2 properties
public string Column1 { get; set; }
public string Column2 { get; set; }
}
and then you store in a List<Foo>:
List<Foo> _items = new List<Foo>();
_items.Add(new Foo { Column1 = "bar", Column2 = "baz" });
Use a tuple struct like KeyValuePair
List<KeyValuePair<string, string>> _items = new List<KeyValuePair<string, string>>();
_items.Add(new KeyValuePair<string, string>(foo, bar));
I would use a class
List<MyDataClass> _items = new List<MyDataClass>();
public class MyDataClass
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
You can either create a new class to hold the data, Or you could use the built in Tuple<> class. http://msdn.microsoft.com/en-us/library/system.tuple.aspx
Also if one of the columns contains a unique ID of some sort, you could also consider using a Dictionary<>.
It's about how to retrieve the data from the new two columns list
List<ListTwoColumns> JobIDAndJobName = new List<ListTwoColumns>();
for (int index = 0; index < JobIDAndJobName.Count;index++)
{
ListTwoColumns List = JobIDAndJobName[index];
if (List.Text == this.cbJob.Text)
{
JobID = List.ID;
}
}
I know this question is pretty old and by now you probably got your answer and have figured out what you need but I wanted to add something that might help someone in the future.
The best current answer is frankly from #csharptest.net but it has a serious performance drawback and so here is my approach a la his answer based on a suggestion to use Dictionary<TKey, TValue>
private Dictionary<string, string> _items = new Dictionary<string, string>();
// if you need to check to see if it exists already or not
private void AddToList(string one, string two)
{
if (!_items.ContainsKey(one))
_items.Add(one, two);
}
// you can simplify the add further
private void AddToList(string one, string two)
{
_items[one] = two;
// note if you try to add and it exists, it will throw exception,
// so alternatively you can wrap it in try/catch - dealer's choice
}
you can also make array of list
List<string> [] list= new List<String> [];
list[0]=new List<string>();
list[1]=new List<string>();
list[0].add("hello");
list[1].add("world");
You could do this:
List<IList<string>> cols = new List<IList<string>>();
You can set how many columns you want.
cols.Add(new List<string> { "", "", "","more","more","more","more","..." });