Returning custom object in LINQ query - c#

I have written this query:
private IDictionary<string, ReftableCache> Lookup(List<string> list)
{
var query = (from r in ReftableCached
where list.Contains(r.Description)
select new ReftableCache
{
RefTableName = r.RefTableName,
Reftable_K = r.Reftable_K,
Description = r.Description
});
return query.ToDictionary(r => r.Reftable_K, ????);
}
But I have problem with the part in ????
My goal is two have a dictionary, keyed by Reftable_K and the value pair of the key to be the object we created for it, but I can't figure out the syntax.

The ToDictionary() method takes two lambda expressions, the key and the value.
For both expressions you get the respective object from the collection as context.
So, you could try this:
private IDictionary<string, ReftableCache> Lookup(List<string> list)
{
// execute the query
var result = (from r in ReftableCached
where list.Contains(r.Description)
select new ReftableCache
{
RefTableName = r.RefTableName,
Reftable_K = r.Reftable_K,
Description = r.Description
})
.ToList();
// convert to dictionary
return result.ToDictionary(r => r.Reftable_K /*key*/
, v => v /*value*/);
}
You could also use the new object in the value part, for example:
return result.ToDictionary(r => r.Reftable_K /*key*/
, v => new ReftableCache
{
RefTableName = r.RefTableName,
Reftable_K = r.Reftable_K,
Description = r.Description
} /*value*/);

It must work for you: (If you want to set the object itself as the value)
return query.ToDictionary(r => r.Reftable_K);
This overload of the ToDictionary takes an anonymous method as input. This anonymous method is actually a key selector that will select the key from the list and set it as the key in the dictionary object. For example, Reftable_K from the ReftableCache is selected as the key for the result in the dictionary and the value is the ReftableCache object itself.
Additional:
May be this method has confused you a bit. To make things more clear I want to explain one thing more. While creating a new dictionary we must specify the Key and the Value. In the example given above, the CLR automatically returns the instance of the object as value. If you want to specify the value as you want, you can add second anonymous function as input to the method. Like that:
return query.ToDictionary(r => r.Reftable_K, r => r);
return query.ToDictionary(r => r.Reftable_K, r => r.Column2);
// and so on...

You can use:
return query.ToDictionary(r => r.Reftable_K, r => r);
You can also just use the overload that automatically returns the item as the value:
return query.ToDictionary(r => r.Reftable_K);
This makes Reftable_K the key, and the ReftableCache instance the value.

Related

IGrouping <decimal,string> does not contain a defintion for 'name'

Using LINQ and lambda expressions, I am trying to write data that I have pulled to a text file.
using (var contextDb = new TimoToolEntities())
{
using (var writeFile = new StreamWriter(saveTo))
{
var randomData = contextDb.WorkCenter_Operations.Where(d => d.Job_Number >= 1 && d.Part_Number.Length >= 1 && d.Oper_Number >= 1 )
.OrderBy(d => d.Oper_Number)
.GroupBy(d => d.Job_Number , d => d.Part_Number ).ToList();
foreach (var record in randomData)
{
Console.WriteLine(record.Job_Number + "," + record.Part_Number); // error here
}
}
Console.ReadLine();
}
I am getting the error the 'IGrouping does not contain a definition for 'name' and no extension method 'name' accepting a first argument of type 'IGrouping' could be found.
I have looked around and believe that the objects are anonymous, but I haven't been able to find a fix that will work.
When you use this overload of GroupBy
.GroupBy(d => d.Job_Number , d => d.Part_Number )
the first lambda is a key selector (you group by Job_Number) and the second one is a value selector. Your record will be a collection of Part_Number with Job_Number as a key.
This MSDN example illustrates the basic usage:
// Group the pets using Age as the key value
// and selecting only the pet's Name for each value.
IEnumerable<IGrouping<int, string>> query =
pets.GroupBy(pet => pet.Age, pet => pet.Name);
// Iterate over each IGrouping in the collection.
foreach (IGrouping<int, string> petGroup in query)
{
// Print the key value of the IGrouping.
Console.WriteLine(petGroup.Key);
// Iterate over each value in the
// IGrouping and print the value.
foreach (string name in petGroup)
Console.WriteLine(" {0}", name);
}
Your intent is not 100% clear, so just in case you actually wanted to group by multiple fields, use a different overload like this:
.GroupBy(d => new { d.Job_Number, d.Part_Number })
Then your record will be a collection of whatever your data is and will have an anonymous key where you can access for example record.Key.Job_Number

Returning a Dictionary<string, string> from a linq query

I have a table with 2 columns defined as varchar(50): Column1 and Column2. I want to return a dictionary of <string, string> where each row is in the dictionary and where Column1 is the key and Column2 is the value. This is what I have:
public Dictionary<string, string> LoadAllTheDataFromDB()
{
using (MyDC TheDC = new MyDC())
{
return (from c in TheTable
select new Dictionary<string, string>()
{
//stuck here
}).FirstOrDefault();
}
}
How do I make it that the dictionary is filled?
Try this:
var dict = TheTable.Select( t => new { t.Col1, t.Col2} )
.ToDictionary( t => t.Col1, t => t);
Remember in select lambda you will perform projection and create some anonymous object. Then in ToDictionary you will pass two parameters: First Parameter is a lambda to specify the key; in code above we are choosing Col1 to be the key. Second parameter is a lambda to specify the value; in code above we are choosing the object itself to be the value.
If you want the value to be an anonymous type, change the 2nd lambda like this:
ToDictionary( t => t.Col1, t => new { t.Col2 });
If you want the value to be a type you have defined, change the 2nd lambda like this:
ToDictionary( t => t.Col1, t => new YourType { Prop1 = t.Col2 });
Since you just need value of one first row why not to do that first:
var row = TheTable.FirstOrDefault();
And than just construct that dictionary if you got the result:
return row == null ? null :
new Dictionary<string,string>{ {row.Column1, row.Column2 } };

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!

Syntax error when performing OrderBy<T> on an IEnumerable List

The error message I receive is:
At least one object must implement IComparable
The code causing this is below:
private static IEnumerable<Result> setOrderBy(IEnumerable<Result> value, string order)
{
if (order.Equals("ASC"))
{
//value = value.OrderBy(c => c, new SearchService.ResultComparer<Attribute>());
value = value.OrderBy<Result>(o => o.StringAttributes.Where(p => p.AttributeName == "Title"), new SearchService.ResultComparer<Attribute>());
//value = value.OrderBy(o => o.StringAttributes.Where(p => p.AttributeName == "Title"), new SearchService.ResultComparer<AttributeItem>()));
}
if (order.Equals("DESC"))
{
value = value.OrderByDescending(c => c, new SearchService.ResultComparer<Attribute>());
//value = value.OrderByDescending(o => o.StringAttributes.Where(p => p.AttributeName == "MatterName"));
}
return value;
}
A little background:
In my MVC2 application, I perform a search in my Search controller. When I send my results to the Results view, I am trying to order the results alphabetically, in ascending or descending order.
However, when I write out the logic to set the OrderBy property for my result object I get the squiggly red line underneath the code (as seen in VS2008). For some reason the method doesn't like the data model I am trying to do a sort upon. Each Result object has various properties, one of which is a list of attributes of type string (hence the name StringAttributes) I am trying to sort each Result object in my IEnumerable collection by the value of one of the String Attributes which is present in ALL of my result records.
Help please!
I think you need to use First() or Single instead of Where() in the place where you are picking out the Attribute to order by. At the moment you are asking OrderBy to calculate order using an IEnumerable<Attribute>, rather than a particular attribute.
value = value.OrderBy<Result>(o => o.StringAttributes.Single(p => p.AttributeName == "Title"), new SearchService.ResultComparer<Attribute>());
or
value = value.OrderBy<Result>(o => o.StringAttributes.First(p => p.AttributeName == "Title"), new SearchService.ResultComparer<Attribute>());

Categories

Resources