I have the following problem:
public class AwesomeClass
{
public string SomethingCool { get; set; }
public string SomethingUseless { get; set; }
}
I have a class which contains a number of properties, and I need to convert a list of these classes into a list of strings, where the string represents a property in the class.
List<AwesomeClass> stuff = new List<AwesomeClass>();
//Fill the stuff list with some tings.
List<string> theCoolStuff = //Get only the SomethingCool property in the AwesomeClass list.
The reason why I need to convert this to a list of strings is because I have a method which takes a List as a parameter, but the SomethingCool property contains the data that I need for this list.
Note: I could use a foreach loop on the list and populate the List of strings but I'm looking for a more elegant method, perhaps LINQ can do this for me?
You can simply use Select:
var theCoolStuff = stuff.Select(x => x.SomethingCool).ToList();
What Select does is a projection, it projects each item and transforms (not convert) them into another form.
Note that you can even:
List<string> theCoolStuff = stuff.ConvertAll(x => x.SomethingCool);
because the List<T> has a special "conversion" method :-)
foreach (AwesomeClass member in stuff)
{
theCoolStuff.Add(member. SomethingCool)
}
Related
I have below class
public class HydronicEquipment
{
public List<LibraryHydronicEquipment> Source { get; set; }
public List<LibraryHydronicEquipment> Distribution { get; set; }
public List<LibraryHydronicEquipment> Terminals { get; set; }
}
and then i have the below class for "libraryHydronicEquipment"
public class LibraryHydronicEquipment : IEquipmentRedundancy
{
public string Name { get; set; }
public RedundancyStatus RedundancyStatus { get; set; }
public EquipmentRedundancy EquipmentRedundancy { get; set; }
}
I am trying to concatenate the list of "LibraryHydronicEquipment" objects available from all three properties (i.e) from source, distribution and terminal and General concatenate method will looks like as this below
var source = hydronicEquipment.Source;
var distribution = hydronicEquipment.Distribution;
var teriminals = hydronicEquipment.Terminals;
Source.Concat(Distribution).Concat(Terminals)
I am trying to achieve the same using reflection and the code looks like as below
foreach (var (systemName, hydronicEquipment) in hydronicSystemEquipment)
{
bool isFirstSystem = true;
var equipmentList = new List<string> { "Source", "Distribution", "Terminals" };
var redundancyequipmentList = GetRedundancyEquipment(hydronicEquipment, equipmentList);
}
and the method GetRedundancyEquipment is looks like below
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
dynamic equipmentResults = null;
foreach(var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults = equipmentRedundancies.Concat(componentList);
}
return equipmentResults;
}
The problem here is even though i have Source is having list of objects and Distribution is having list of objects, the equipmentResults is giving only one object instead of list of concatenated objects.
I am trying to return the IEnumerable<IEquipmentRedundancy> at the end using reflection method but it seems not working with the above code.
Could any one please let me know how can i achieve this, Many thanks in advance.
GetRedundancyEquipment should preserve your values instead of reassign the reference with each iteration. Here's the fixed version:
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
var equipmentResults = new List<IEquipmentRedundancy>();
foreach (var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults.AddRange(equipmentRedundancies.Concat(componentList));
}
return equipmentResults;
}
If we look at what you're doing in GetRedundancyEquipment() it becomes clear.
First you create equipmentRedundancies = new List<IEquipmentRedundancy>();
Then you never modify equipmentRedundancies - e.g. via Add(). It remains an empty list until it goes out of scope and is garbage collected.
In a loop you then repeatedly make this assignment equipmentResults = equipmentRedundancies.Concat(componentList);
That is to say: Assign to equipmentResults the concatenation of componentList to equipmentRedundancies.
Note that Concat() is a lazily evaluated linq method. When you actually enumerate it results are produced. It doesn't modify anything, it's more like a description of how to produce a sequence.
So each time through the loop you're assigning a new IEnumerable that describes a concatentaion of an empty list followed by the property that you retrieved with reflection to equipmentResults. Then at the end you return the final one of these concatenations of an empty list and retrieved property.
If you want all of them together, you should concatenate each of them to the result of the previous concatenation, not to an empty list.
Can't seem to work this out.
I have a student class that has a property access modifier
{
public class SerialNumbers
{
public string SerialNumber { get; set; }
}
}
Now i have made a trip to the database and returned by results from a datatable to a string[]
string[] serialArray = dt.Rows.OfType<DataRow>().Select(k => k[1].ToString()).ToArray();
which outputs the desired results. Now im trying to save the results to my model via the List so i am trying to convert my string[] to a list of type SerialNumber but i keep getting invalid cast
List<SerialNumbers> serial = serialArray.Cast<SerialNumbers>().ToList();
Can anybody tell me why this doesn't work, Thanks in advance.
That Cast<T>() call is essentially (SerialNumber)item which fails because your items are strings not serial numbers.
You need a projection from strings which will be like
List<SerialNumbers> serial = serialArray.Select(s => new SerialNumbers { SerialNumber = s }).ToList();
You can't cast String Array to custom type list with build-in methods. I think the simplest way to do it with foreach loop to populate your custom type list.
List<SerialNumbers> serial = new List<SerialNumbers>();
foreach (var item in serialArray)
{
serial.Add(new SerialNumbers() {SerialNumber = item});
}
Your SerialNumbers class has no way to parse the string type, simply implement a method void Parse(string[]); or void Parse(string); in the class and use it.
I have a List "RootObject" that contains another list "Components". So basically each RootObject could have many Components. I need to get all Distinct values out of "Components" to bind to a ListView.
public class RootObject
{
public string id { get; set; }
public List<string> Components { get; set; }
public string name { get; set; }
}
I think I may need to use SelectMany but not sure how to get them...
For example I have my root object into
mylist = deserial.Deserialize<List<RootObject>>(response);
This works. I then need to get a list of Components into a ListView
`ListView.DataSource = //get list of Components`
This gets the distinct components into a list:
var distinctComponents = rootObjects
.SelectMany(r => r.Components)
.Distinct()
.OrderBy(c => c)
.ToList();
I don't know which GUI technology you are using, but a winforms ListView has no simple binding mechanism. You need to add items and subitems manually. So, you could also drop the ToList() and enumerate the query directly in a foreach-statement.
for distinct components, you'll need something like this:
List<RootObject> values = new List<RootObject>(); // assuming this is the collection
var distinctComponents = values.SelectMany(r => r.Components).Distinct().ToList();
I am using C# to create an app and want to be able to easily do a foreach() loop through my 120 strings that i have. All these strings are constructed as below:
public class _currentweekapi
{
public string _1_artist { get; set; }
public string _2_artist { get; set; }
public string _3_artist { get; set; }
...
}
Is it possible to be able to do a foreach() loop to loop through all the values. The strings are set automatically from a JSON feed so i cannot get the values straight into a list.
I have read up on the topic and understand there are ways through 'Reflection' but I am new to C# and reading up on it doesn't make any sense to me.
Could someone please help me with a solution to this stating exactly how I could implement this?
Using reflection is quite easy - try this for a start
var properties = typeof(_currentweekapi).GetProperties();
foreach(PropertyInfo info in properties) {
if (info.Name.EndsWith("artist") {
// do something amazing
}
}
see - http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx for more information
The first thing you should be trying to do, is NOT store the strings in explicitly named properties, as your class definition appears to be doing. The most appropriate scenario would seem to be a List<string> type property that can hold them all in your class. This would also mean you already have your list ready to go.So, if you are able to, change the class, or use another, which accepts the JSON feed, and uses .Add() on a property of type List<string>, rather than explicitly setting 120 properties.
Like this:
public class ArtistData
{
public List<string> Artists{ get; set; }
public ArtistData()
{
this.Artists = new List<string>(0);
}
public void PopulateArtists(string jsonFeed)
{
// here something desrializes your JSON, allowing you to extract the artists...
// here you would be populating the Artists property by adding them to the list in a loop...
}
}
Then, you have your list in the property Artists, and can use the list directly, or return it by doing:
string[] artists = myInstance.Artists.ToArray();
However, you seem to have indicated that you cannot change the fact that they end up served to you as individual properties in the class you showed us, so...
Assuming you have no choice, but to start from a class such as the one you showed, here is a way you could do a loop through all those values, just as you asked for, all that will be required is that you pass your class instance into one of the methods below as the one parameter that each requires:
// this will return a list of the values contained in the properties...
public List<string> GetListFromProperties<T>(T instance)
{
Type t = typeof(T);
PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// As a simple list...
List<string> artists = new List<string>(props.Length);
for (int i = 0; i < props.Length; i++)
{
if(!props[i].Name.Contains("_artist")){ continue; }
artists.Add(props[i].GetValue(instance, null).ToString());
}
return artists;
}
// this will return a dictionary which has each artist stored
// under a key which is the name of the property the artist was in.
public Dictionary<string,string> GetDictionaryFromProperties<T>(T instance)
{
Type t = typeof(T);
PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// As a dictionary...
Dictionary<string,string> artists = new Dictionary<string,string>(props.Length);
for (int i = 0; i < props.Length; i++)
{
if(artists.ContainsKey(props[i].Name) || !props[i].Name.Contains("_artist")){ continue; }
artists.Add(props[i].Name, props[i].GetValue(instance, null).ToString());
}
return artists;
}
Either one should help, but do not use the dictionary one, as it entails more overhead, unless you really need to know the name of the property each artist came from as well, in which case it is more helpful than a simple list.Incidentally, since the methods are generic, that is, they accept a parameter of type T, the same methods will work for ANY class instance, not just the one you are struggling with now. REMEMBER: although it may appear extremely convenient to you at the moment, this is not necessarily the best way to approach this. Better than this, would be the initial suggestions I made for re-working the class altogether so this sort of thing is not required.
You can do something like this:
List<string> list = new List<string>();
foreach (var property in _currentweekapi.GetType().GetProperties())
{
list.Add(property.GetValue(_currentweekapi, null));
}
If you can assure that there are no other types of properties then this will work:
var list =
typeof(_currentweekapi)
.GetProperties()
.Select(pi => (string)pi.GetValue(x, null))
.ToList();
I have a JSON "multi-level" response that I need to deserialize and from the deserialized classes structure I need to extract all the objects of a certain class.
Below the code I'm using, at the end I find that my result is empty, not populated.
// given these two classes:
[DataContract]
public class ThingsList
{
[DataMember(Name = "status")]
public string Status { get; set; }
[DataMember(Name = "since")]
public double Since { get; set; }
[DataMember(Name = "list")]
public Dictionary<string, ThingsListItem> Items { get; set; }
public DateTime SinceDate { get { return UnixTime.ToDateTime(Since); } }
}
[DataContract]
public class ThingsListItem
{
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "title")]
public string Title { get; set; }
}
// I can deserialize my json to this structure with:
ThingsList results = JsonConvert.DeserializeObject<ThingsList>(e.Result);
// now I need to "extract" only the ThingsListItem objects, and I'm trying this:
var theList = from item in results.Items.OfType<ThingsListItem>()
select new
{
Title = item.Title,
Url = item.Url
};
// but "theList" is not populated.
The points here are (I believe):
- I try to use results.Items.OfType() in order to extract only the ThingsListItem objects, that in the "upper" class are declared in the
public Dictionary Items { get; set; }
row.
Any idea? Tell if it's not clear...
Thanks
Andrea
EDIT: updated my response for clarity.
Since your Dictionary values are of type ThingsListItem you can access them directly by using the Dictionary's Values property. There is no need to use OfType to check their type and extract them. Simply use:
var items = results.Items.Values;
The Values property would return an ICollection<ThingsListItem>. You can then iterate over the results with a foreach. LINQ does not have to be used.
While the Values property described above should be sufficient, I will point out a few issues with your original LINQ query attempt.
1) The following query is probably what you were after. Again, the Dictionary's Values property is key (no pun intended) to accessing the items:
var theList = from item in results.Items.Values
select new
{
Title = item.Title,
Url = item.Url
};
2) Why are you using new? That will return an IEnumerable of anonymous types. You already have a defined class, so why project into a new anonymous type? You should retain the underlying ThingsListItem items by selecting the item directly to get an IEnumerable<ThingsListItem>:
var theList = from item in results.Items.Values
select item;
foreach (var item in theList)
{
Console.WriteLine("Title: {0}, Url: {1}", item.Title, item.Url);
}
You would usually project into a new anonymous type to define a type with data properties you are interested in. Generally you would use them immediately after the query, whereas a selection into an existing class could be used immediately or passed around to other methods that are expecting that type.
Hopefully this has cleared up some questions for you and you have a better idea of using LINQ and when to use the new keyword. To reiterate, for your purposes it seems the Values property should suffice. Using LINQ to select the item is redundant when there are other immediate means to do so.