Show only columns from an string array - c#

I have a Table in my Entities with 370 columns ! Furthermore i have a string Array which is not known before runtime (comes from a website).
e.g.:
string [] columns = {"column1", "column2", "column3"}
How can i fire a linq to my entities which gives me only the result with the giving columns?
I searched for hours, but don`t suceed till now - any suggestions?

This is not something that you can do with Linq-to-Entities. You need to be able to declare the columns in your code.
A better approach would be to create the query in Sql using the column names in your array, and use something like Dapper to map the results to your objects.

You could instantiate an ExpandoObject in your Delegate and use Reflection to get the columns specified in the incoming array.
The following:
List<IDictionary<String, Object>> List = Context.Row_Type.Select(delegate(Row_Type Row) {
IDictionary<String, Object> Out = new ExpandoObject() as IDictionary<String, Object>;
PropertyInfo[] PIs = Row.GetType().GetProperties();
foreach(PropertyInfo PI in PIs) {
if(PI.GetIndexParameters().Length == 0) {
Out.Add(PI.Name, PI.GetValue(Row));
}
}
return Out;
}).ToList();
Will return a List < IDictionary< String, Object>> with all the properties, to return the desired columns just discriminate by PI.Name:
if(PI.Name == "Desired column"){ // Or Array index
// Add to Out:
Out.Add( PI.Name, PI.GetValue(Row) )
}

I'm guessing you don't really want to return a class with unknown column names. Would a dictionary of column names and values work for you? Your query would still have to retrieve all of the columns, but you can only return the ones you care about.
string [] columns = {"column1", "column2", "column3"}
var entity = GetEntity();
var dictionary = columns.ToDictionary(c => c, c => entity.GetType().GetProperty(c).GetValue(entity));
Or, if you have a collection...
var entities = GetEntities();
var results = entities
.Select(e => columns.ToDictionary(c => c, c => e.GetType().GetProperty(c).GetValue(e)));

Related

Use LINQ to convert List of Dictionary to strongly Typed Class object List

I am working on .NET CORE 6 app and I have Dictionary with structured as
Dictionary<string, Dictionary<int, string>> dataDictionary
I have pull list of Dictionary based on List<Dictionary<int, string>> filteredDictionary as below
var filteredDictionary = dataDictionary.Values.ToList();
Each list has 17 dictionary that represent properties of class AIMSchema where each property is represented by key (Int) no. So I know value at dictionary index 0 present TransactionDate, 1 represent MachineCode and so on... The object is I want convert this dictionary list to List<AIMSchema>
I can do in loop as below but I want to do using LINQ
foreach (var schema in dataDictionary.Values)
{
if(schema != null)
{
var aimSchema = new AIMSchema
{
TransactionDate = schema[0],
MachineCode = schema[1],
// ... other properties
}
}
Well, I'm going to provide exactly what you asked for as an answer. The LINQ equivalent of your existing code would look like this:
List<AIMSchema> result = dataDictionary.Values
.Where(schema => schema != null)
.Select(schema => new AIMSchema
{
TransactionDate = schema[0],
MachineCode = schema[1],
// ... other properties
})
.ToList();
The .Where is a filter, equivalent to your if (schema != null), and the .Select is a projection which turns the entry into an AIMSchema object. As we're still dealing with a query on dataDictionary.Values at this stage, the .ToList() materializes the result into a List<AIMSchema>.

How to store the result of a linq query in a KeyDictionary variable

So I have a collection of objects who have multiple properties, two of these are groupname and personname. Now I need to count in the collection how many of each object belong to a certain group and person. So in other words, I need to group by groupname, then personname and then count how many objects have this combination. First I created this
public MultiKeyDictionary<string, string, int> GetPersonsPerGroup(IEnumerable<Home> homes ,List<string> gr, List<string> na)
{
List<string> groups = gr;
groups.Add("");
List<string> names = na;
names.Add("");
List<Home> Filtered = homes.ToList();
Filtered.ForEach(h => h.RemoveNull());
var result = new MultiKeyDictionary<string, string, int>();
int counter1 = 0;
foreach (var g in groups)
{
int counter2 = 0;
foreach (var n in names)
{
int counter3 = 0;
foreach (Home h in Filtered)
{
if (h.GroupName == g && h.PersonName == n)
{
counter3++;
if (counter3 > 100)
break;
}
}
if (counter3 > 0)
{
result.Add(g,n,counter3);
}
counter2++;
}
counter1++;
}
Which may look good, but the problem is that the "home" parameter can contain more than 10000 objects, with more than 1500 unique names and around 200 unique groups. Which causes this to iterate like a billion times really slowing my program down. So I need an other way of handling this. Which made me decide to try using linq. Which led to this creation:
var newList = Filtered.GroupBy(x => new { x.GroupName, x.PersonName })
.Select(y => (MultiKeyDictionary<string, string, int>)result.Add(y.Key.GroupName, y.Key.PersonName, y.ToList().Count));
Which gives an error "Cannot convert type 'void' to 'MultiKeyDictionary<string,string,int>' and I have no idea how to solve it. How can I make it so that the result of this query gets stored all in one MultikeyDictionary without having to iterate over each possible combination and counting all of them.
Some information:
MultiKeyDictionary is a class I defined (something I found on here actually), it's just a normal dictionary but with two keys assosiated to one value.
The RemoveNull() method on the Home object makes sure that all the properties of the Home object are not null. If it is the case the value gets sets to something not null ("null", basic date, 0, ...).
The parameters are:
homes = a list of Home objects received from an other class
gr = a list of all the unique groups in the list of homes
na = a list of all the unique names in the list of homes
The same name can occur on different groups
Hopefully someone can help me get further!
Thanks in advance!
Select must return something. You are not returning but only adding to an existing list. Do this instead:
var newList = Filtered.GroupBy(x => new { x.GroupName, x.PersonName }):
var result = new MultiKeyDictionary<string, string, int>);
foreach(var y in newList)
{
result.Add(y.Key.GroupName, y.Key.PersonName, y.ToList().Count));
}
The reason you are getting error below:
"Cannot convert type 'void' to 'MultiKeyDictionary'
is because you are trying to cast the returned value from Add which is void to MultiKeyDictionary<string,string,int> which clearly cannot be done.
If MultiKeyDictionary requires the two keys to match in order to find a result, then you might want to just use a regular Dictionary with a Tuple as a composite type. C# 7 has features that make this pretty easy:
public Dictionary<(string, string), int> GetPersonsPerGroup(IEnumerable<Home> homes ,List<string> gr, List<string> na)
{
return Filtered.GroupBy(x => (x.GroupName, x.PersonName))
.ToDictionary(g => g.Key, g => g.Count);
}
You can even associate optional compile-time names with your tuple's values, by declaring it like this: Dictionary<(string groupName, string personName), int>.
Your grouping key anonymous object should work fine as a standard Dictionary key, so no reason to create a new type of Dictionary unless it offers special access via single keys, so just convert the grouping to a standard Dictionary:
var result = Filtered.GroupBy(f => new { f.GroupName, f.PersonName })
.ToDictionary(fg => fg.Key, fg => fg.Count());

Dapper: How to read into list of Dictionary from query?

Dapper provides lots of ways mapping data into list of dynamic objects. However in some case I'd like to read data to list of Dictionary.
The SQL may looks like:
"SELECT * FROM tb_User"
As tb_User may change outside, I don't know what columns will return in result. So I can write some code like this:
var listOfDict = conn.QueryAsDictionary(sql);
foreach (var dict in listOfDict) {
if (dict.Contains("anyColumn")) {
// do right thing...
}
}
Is there any built-in methods for Dapper to do this conversion?
You can cast each row as IDictionary:
var row = (IDictionary<string, object>)conn.Query("select foo = 1, bar = 'bar'").First();
Assert.That(row["foo"], Is.EqualTo(1));
Assert.That(row["bar"], Is.EqualTo("bar"));
You could use the Cast extension method from System.Linq
IEnumerable<IDictionary<string, object>> rows;
rows = connection.Query(sqlRequest).Cast<IDictionary<string, object>>();
foreach (var row in rows)
{
var columnValue = row['columnName']; // returns the value of the column name
}
You can just assign aliases to your query so that it matches the Key and Value properties of a KeyValuePair and then use the .ToDictionary method like this:
var dict = db.Query<KeyValuePair<string, int>>(#"
SELECT COMMUNITY_TYPE As Key, COUNT(*) AS Value
FROM SNCOMM.COMMUNITY
GROUP BY COMMUNITY_TYPE")
.ToDictionary(x => x.Key, x => x.Value);
Now you have a Dictionary<string, int> without any manual converting.

Dynamically create anonymous object from list values c#

I have a list (or can be array) of strings that I want to dynamically create an anonymous object from. How do I do this?
var dataSet = new DataSet();
dataSet.ReadXml(#"");
var dataTable = dataSet.Tables[0];
var dataRow = dataTable.Rows[0];
var keys = new List<string> {"Column1","Column2"};
var result = new {keys[0] = dataRow[keys[0]], keys[1] = dataRow[keys[1]]}
So that list named "keys" is going to be created outside this method and can contain 1 to many values. I tried creating a dictionary and looping through the list and adding key/value pairs to the dictionary but then I couldnt figure out how to convert the dictionary back to an anonymous type. I also experimented with the expando objects but that didn't seem to get me any farther.
I must be able to return an anonymous type as the result of this method will be using with the GroupBy clause of a LINQ query.
Here is the method I had to dynamically create the dictionary:
public object Key(DataRow dataRow, List<String> keys)
{
var dictionary = new IDictionary<string, object>;
foreach (string key in keys)
{
dictionary.Add(key, dataRow[key]);
}
return dictionary;
}
Here is my LINQ query:
var duplicates = dataTable.AsEnumerable().GroupBy(r => Key(r, keys)).Where(c => c.Count() > 1).ToList();
The GroupBy clause works if I hardcode in an anonymous type from the Key() method. Basically I just need the GroupBy clause to be dynamically set based upon the values in the keys list.
Stripping down your question, what you want is to be able to group a list of items based on a runtime property which could be composed of one or more properties of that item. In essence, it means you need a selector function (which is your Key method) that transforms an item into a key.
In order for GroupBy to work, it needs to be able to compare any two instances of the key to see if they're equal. This means the key needs to implement a meaningful Equals() method, or you need an IEqualityComparer implementation that does the work for you. In this case I wouldn't bother with creating a new Key, just write an Equality Comparer that can compare two DataRows directly:
var duplicates = dataTable
.AsEnumerable()
.GroupBy(r => r, new MyDataRowComparer(keys))
.Where(c => c.Count() > 1)
.ToList();
internal class MyDataRowComparer : IEqualityComparer<DataRow>
{
private readonly string[] _keys;
public MyDataRowComparer(string[] keys)
{
_keys = keys; // keep the keys to compare by.
}
public bool Equals(DataRow x, DataRow y)
{
// a simple implementation that checks if all the required fields
// match. This might need more work.
bool areEqual = true;
foreach (var key in _keys)
{
areEqual &= (x[key] == y[key]);
}
return areEqual;
}
public int GetHashCode(DataRow obj)
{
// Add implementation here to create an aggregate hashcode.
}
}

LINQ query and Array of string

I have a array of string say:
String[] Fields=new String[]{RowField,RowField1}
In which I can use the below query to get the values by specifying the values is query i.e RowField and RowField1:
var Result = (
from x in _dataTable.AsEnumerable()
select new
{
Name = x.Field<object>(RowField),
Name1 = x.Field<object>(RowField1)
})
.Distinct();
But if suppose I have many values in the Array like:
String[] Fields= new String[]
{
RowField,
RowField1,
RowField2,
.......
RowField1000
};
How can I use the query here without specifying each of the rowfield in the query?
How can i iterate through the array items inside the LINQ?
var Result = (
from x in _dataTable.AsEnumerable()
select (
from y in Fields
select new KeyValuePair<string, object>(y, x))
.ToDictionary())
.Distinct(DictionariesComparer);
You'll also need to write your own .ToDictionary() extension method and DictionariesComparer method (as Dictionary doesn't implement IEquatable).
Essentially, you want to retrieve specific fields from a DataTable without hardcoding the field names.
The following code will return a single dictionary object per row with the fields you specify in your array. There is no need to create additional extension methods or comparers:
var result = (from row in _dataTable.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value));
The inner select picks the field values you need from each table row and stores them in the projection variable. The outer select converts this variable in a Dictionary
You can iterate over the result to get specific fields like this:
foreach (var row in result)
{
Console.WriteLine(row["field1"]);
}
EDIT:
The above code doesn't return distinct values. It is possible to return distinct values without writing a special comparer using group by but the code is not very pretty:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new { Name = fieldName, Value = row[fieldName] }
group projection by projection.Aggregate((v, p) =>
new {
Name = v.Name + p.Name,
Value = (object)String.Format("{0}{1}", v.Value, p.Value)
}) into g
select g.FirstOrDefault().ToDictionary(p=>p.Name,p=>p.Value));
The Aggregate creates a new projection whose Name and Value properties are the concatenation of all name and value fields. The result of the aggregate is used to group all rows and return the first row of each group. It works but it is definitely ugly.
It would be better to create a simple DictionaryComparer like the following code:
public class DictionaryComparer<TKey,TValue>: EqualityComparer<Dictionary<TKey,TValue>>
{
public override bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
//True if both sequences of KeyValuePair items are equal
var sequenceEqual = x.SequenceEqual(y);
return sequenceEqual;
}
public override int GetHashCode(Dictionary<TKey, TValue> obj)
{
//Quickly detect differences in size, defer to Equals for dictionaries
//with matching sizes
return obj.Count;
}
}
This allows you to write:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value))
.Distinct(new DictionaryComparer<string, object>());
There is no foreach linq expression. I typically create my own extension method
Something along the lines of:
public static void Foreach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach(T t in items)
{
action(t);
}
}
However beware if you're planning on using this with Linq2SQL as it could create a lot of db hits!

Categories

Resources