I know I'm missing something fundamental with either generics or covariance, and I was hoping there is a better way to do what I am doing.
I have a method that takes a list of domain objects and turns it into an HTML table:
public String GenerateTable(List<object> Data, String[] Properties,
String[] ColumnHeaders = null)
{
}
When I call the method, I find myself having to do this:
List<Customer> cust = GetCustomers();
List<object> oCust = new List<object>;
foreach (Customer c in cust)
oCust.Add((object)c);
string table = GenerateTable(oCust, new string[] { "CustNbr", "CustName" });
I believe with covariance I can simply:
List<object> oCust = cust;
But I'm looking for a better solution all-around -- eliminate the necessity to create a completely new list each time I run this method. It's not a performance or memory issue, as these lists are always relatively small, but I'd like to understand what is the best (or at least better) way to accomplish this.
You should change GenerateTable to accept an IEnumerable of objects instead of a list. Then you won't have to convert your Customer list to a list of objects.
public String GenerateTable(IEnumerable<object> Data, String[] Properties, String[] ColumnHeaders = null)
The problem with your original version is that GenerateTable could attempt to add a non-Customer object to the List. IEnumerable works because it is read only. You can read more about it here, if you are interested.
Covariance is only supported in generic interfaces. Since it looks like an IEnumerable would be sufficient you can try to not use a generic at all.
public string GenerateTable(IEnumerable data, string[] properties, string[] columnHeaders = null)
Alternately, you could set up a generic transformation method
public string GenerateRow(Customer customer) { // convert one object here}
public string GenerateTable<T>(List<T> objects, Func<T,string> rowGenerator)
{
// table boilerplate
foreach(var obj in objects) {
output.Append(rowGenerate(customer))
}
}
and then call it with
var table = GenerateTable(customerList, GenerateRow);
to generate your table.
Related
I have a List of dynamic objects that I am trying to use dynamic Linq on. I am using dynamic objects because I do not know the properties that will be coming into the object. Linq works on my dynamic object, but, to avoid giant hard coding if statements, I would like to use dynamic Linq to search my list. The top half of the code snippet works but I need it to work dynamically so I can create a query string from my properties and filter that way.
public List<dynamic> GetFilteredLocationData(List<dynamic> locationData, string searchTerm){
//Does work
List<dynamic> totalResults = locationData.Where(x => x.Street.ToLower().Contains(searchTerm.ToLower()) ||
x.Street.ToLower().Contains(searchTerm.ToLower()) ||
x.Zip.ToLower().Contains(searchTerm.ToLower()));
//Does not work
var testQueryString = "(Street == \"king\")";
var testResult = locationData.Where(testQueryString);
return totalResults;
}
The runtime error I receive: No property or field 'Street' exists in type 'Object'
That error makes sense as object by default doesn't contain 'Street' but I'd expect the dynamic Linq to behave like the code above it. Is there something I am doing wrong here, or should I take a different approach? I can provide more detail if needed.
Thanks in advance!
Finally I got a working solution! It may not be the most efficient but it works for my needs and allows me to keep the dynamic nature I was hoping to retain. The solution was to drop Linq entirely and use a good old for-each loop. The Important part was the IDictionary which allowed me to search each row for the key value pair. This is the same functionality I was going for, just ditched linq.
public List<dynamic> GetFilteredLocationData(List<dynamic> locationData, string searchTerm){
List<dynamic> totalResults = new List<dynamic>();
List<string> locationProperties = new List<string> {"dynamic properties here, this was filled by call to DB for info pertaining to certain location combined with unique data"}
foreach (var locData in locationData)
{
var currentLoc = locData;
var currentLocDict = (IDictionary<string, object>)currentLoc;
bool containsSearchTerm = CheckIfLocationContainsSearch(currentLocDict, allLocationProperties, searchTerm);
if (containsSearchTerm)
{
totalResults.Add(locData);
}
}
}
public bool CheckIfLocationContainsSearch(IDictionary<string,object> location, List<string> locationProperties, string searchTerm){
foreach (var locProp in locationProperties)
{
if (location[locProp].ToString().ToLower().Contains(searchTerm))
{
return true;
}
}
return false;
}
I want to modify some strings that are contained in an object like say an array, or maybe the nodes in an XDocument (XText)XNode.Value.
I want to gather a subset of strings from these objects and modify them, but I don't know at runtime from what object type they come from.
Put another way, let's say I have objects like this:
List<string> fruits = new List<string>() {"apple", "banana", "cantelope"};
XDocument _xmlObject;
I want to be able to add a subset of values from the original collections to new lists like this:
List<ref string> myStrings1 = new List<ref string>();
myStrings1.Add(ref fruits[1]);
myStrings1.Add(ref fruits[2]);
List<ref string> myStrings2 = new List<ref string>();
IEnumerable<XNode> xTextNodes = getTargetTextNodes(targetPath); //some function returns a series of XNodes in the XDocument
foreach (XNode node in xTextNodes)
{
myStrings2.Add(((XText)node).Value);
}
Then change the values using a general purpose method like this:
public void Modify(List<ref string> mystrings){
foreach (ref string item in mystrings)
{
item = "new string";
}
}
Such that I can pass that method any string collection, and modify the strings in the original object without having to deal with the original object itself.
static void Main(string[] args)
{
Modify(myStrings1);
Modify(myStrings2);
}
The important part here is the mystrings collection. That can be special. But I need to be able to use a variety of different kinds of strings and string collections as the originals source data to go in that collection.
Of course, the above code doesn't work, and neither does any variation I've tried. Is this even possible in c#?
What you want is possible with C#... but only if you can fix every possible source for your strings. That would allow you to use pointers to the original strings... at a terrible cost, however, in terms of memory management and unsafe code throughout your application.
I encourage you to pursue a different direction for this.
Based on your edits, it looks like you're always working with an entire collection, and always modifying the entire collection at once. Also, this might not even be a string collection at the outset. I don't think you'll be able to get the exact result you want, because of the base XDocument type you're working with. But one possible direction to explore might look like this:
public IEnumerable<string> Modify(IEnumerable<string> items)
{
foreach(string item in items)
{
yield return "blah";
}
}
You can use a projection to get strings from any collection type, and get your modified text back:
fruits = Modify(fruits).ToList();
var nodes = Modify( xTextNodes.Select(n => (XText)n.Value));
And once you understand how to make a projection, you may find that the existing .Select() method already does everything you need.
What I really suggest, though, is that rather than working with an entire collection, think about working in terms of one record at a time. Create a common object type that all of your data sources understand. Create a projection from each data source into the common object type. Loop through each of the objects in your projection and make your adjustment. Then have another projection back to the original record type. This will not be the original collection. It will be a new collection. Write your new collection back to disk.
Used appropriately, this also has the potential for much greater performance than your original approach. This is because working with one record at a time, using these linq projections, opens the door to streaming the data, such that only one the one current record is ever held in memory at a time. You can open a stream from the original and a stream for the output, and write to the output just as fast as you can read from the original.
The easiest way to achieve this is by doing the looping outside of the method. This allows you to pass the strings by reference which will replace the existing reference with the new one (don't forget that strings are immutable).
And example of this:
void Main()
{
string[] arr = new[] {"lala", "lolo"};
arr.Dump();
for(var i = 0; i < arr.Length; i++)
{
ModifyStrings(ref arr[i]);
}
arr.Dump();
}
public void ModifyStrings(ref string item)
{
item = "blah";
}
Is it possible select an anonymous type via list of properties as parameter. The method should look like:
public void TestLinq(List<"Properties"> properties, List<Data> data)
{
var dat = from d in data select new { properties };
}
I know the description sounds clumsy but I hope I get some help.
It would be important to know the term I have to look for this topic.
You can use the Dynamic LINQ query library (download the sample) to create the list of properties in your projection, like so:
public dynamic TestLinq(IEnumerable<Data> data, IEnumerable<string> properties)
{
// Validate parameters.
if (properties == null) throw new ArgumentNullException("properties");
if (data == null) throw new ArgumentNullException("data");
// Construct the field list.
var fields = new StringBuilder();
foreach (string p in properties) fields.AppendFormat("{0},", property);
// Throw an exception if there are no items.
if (fields.Length == 0) throw new ArgumentException(
"The properties enumeration contains no elements.", "properties");
// Remove the last comma.
fields.Length--;
// Select the items and return. Create the
// projection here.
return data.Select("new(" + fields + ")");
}
Note that the return type is of type dynamic, so you'll have no compile-time checking, and unless you're duck-typing, you probably won't have much knowledge of the fields.
You might be better off creating strong types for this, depending on your needs (if this is based on user-input, then you can't obviously).
Here you go, this is based on this answer https://stackoverflow.com/a/5310828/491950
List<string> properties = new List<string>() { {"ResultPrefix"}, {"ProfileResult"}};
foreach (dynamic d in ListProperties(properties, cellValues))
{
Console.WriteLine(d.ResultPrefix);
}
public static List<dynamic> ListProperties(List<string> properties, List<ChemistryResult> chemistryResults)
{
List<dynamic> output = new List<dynamic>();
foreach (ChemistryResult chemistryResult in chemistryResults)
{
IDictionary<string, Object> result = new ExpandoObject();
foreach (string property in properties)
{
PropertyInfo propertyInfo = typeof(ChemistryResult).GetProperty(property);
result[property] = propertyInfo.GetValue(chemistryResult);
}
output.Add(result);
}
return output;
}
You cannot use anonymous types in a method signature. It cannot be used as a parameter or the return type.
What you could do, is declare the parameter as dynamic, but dynamic can get really sticky, so I recommend avoiding it. You could have a List<dynamic> parameter, then you will be able to access members of the type, but you will not get type checking at compile time.
Another option it to use IEnumerable or IList. Using either of these will allow you to access the members of the collection without knowing the type. This is safer, as you have all of your compile time checks, but will not allow you to access members or the anonymous type.
But really, you should just convert your anonymous type into a real class so you can make your life easier.
I am sorry for the confusion. The outcome should be a csv that's right. The user should be able to define the order of the columns. But for me it was very difficult to formulate a good question. I am looking for a solution with expresisons not with reflection. My Idea was to generate a List of anonymous objects (with the right order) and out of them I wanted to create the csv. So I know the following is working:
public void Get(List<Value> data,Expression<Func<Value, T>> converter)
{
var dat = from d in data
select
new
{
converter
};
}
Is it possible to safe the Expression> converter in a property and combine many of them to one? So I would get the corret order
The question maybe a little confusing, but it's hard to make clear this question in a subject title.
I have method declared and implemented like this:
public IList<string> GetBookTitles()
{
IList<string> bookTitles = new List<string>();
// do something to populate the bookTitles list.
return bookTitles;
}
Why can't I pass the result of this method to a List<string>? After all, List<string> is a kind of IList<string>.
Well, for starters, just look at the members of IList and compare it with List. List has methods that an IList doesn't. (List has a BinarySearch method that IList doesn't, just as a single example.)
Arrays also implement IList, as an example. An array however is not a List, so you can't, and shouldn't, be able to pass a string[] to a method that accepts a List<string>.
You have a few possible solutions. One would be to just change your method to return a List<string> rather than an IList<string> (that's what I'd suggest). If that's what you really need then you shouldn't be restricting the return type to IList<string>. Another (poorer) option would be to cast the result back to a List<string> before passing it to the next method, since you happen to know that it's what the underlying type really is.
After all, List<string> is a kind of IList<string>.
But there are also other kinds of IList<String>.
What if your method were to return an IList<String> which is a ReadOnlyCollection<String> instead?
IList<string> x = new ReadOnlyCollection<string>();
List<string> y = x; //Huh?
The compiler uses the signature of your methods, not the implementation when deciding if you can assign the result of GetBookTitles to your variable, so it can't know that the result will in fact be a List. If it would allow you to do such a thing, then you could write something like this:
List<string> myBooks = GetBookTitles();
myBooks.Sort();
In your example you could do this, and in fact you can if you cast the result of your method:
List<string> myBooks = (List<string>)GetBookTitles();
But then one day you could decide that your book collection is not modifiable, and you rewrite your method as follows:
public IList<string> GetBookTitles()
{
IList<string> tmp = new List<string>();
// do something to populate the bookTitles list.
IList<string> bookTitles = new ReadOnlyCollection<string>(tmp);
return bookTitles;
}
ReadOnlyCollection does not implement Sort, so your app would compile, but would crash at runtime.
Using the cast approach it would crash when trying to do the cast, but in this case you are taking the responsibility of deciding that that kind of cast is feasible and do not have the compiler trying to guess.
A better approach could be to use as instead of the cast and chek for null. I.e.:
List<string> myBooks = GetBookTitles() as List<string>;
if (myBooks != null)
myBooks.Sort();
You should be able to, you just need an explicit conversion.
List<string> foo = (List<string>)GetBookTitles()
should do it.
The interface may be implemented in various classes which are not same. So, it will be difficult to find the respective class.
You can type cast from IList to List!!!
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.