c# linq question - c#

I have a data table and I want to select all distinct names from the result. I wrote following linq query for it.
var distinctRows = (from DataRow myDataRow in myDataTable.Rows
select new { col1 = myDataRow ["Name"]}).Distinct();
Now how can I iterate through distinctRows? Seems like I cannot do foreach(DataRow Row in distinctRows), It gives me "Cannot convert type 'AnonymousType#1' to 'System.Data.DataRow'" error

Since you're only selecting one field, you don't need an anonymous type here. Just select the names and then iterate over the distinct ones. To wit:
var distinctNames = (from DataRow myDataRow in myDataTable.Rows
select myDataRow.Field<string>("Name")
).Distinct();
foreach(var name in distinctNames) {
Console.WriteLine(name);
}
Note that the error makes it very clear what the problem is here. You are trying to convert an instance of an anonymous type to an instance of DataRow and that is impossible. Without changing your code, you could iterate this as
foreach(var item in distinctRows) {
Console.WriteLine((string)item.col1);
}
But I would change this as per the above as you don't need the anonymous type and your variable names and field names are poor.

Those aren't DataRows; they're anonymous objects.
To loop through them, you need to declare the variable using the var keyword.
However, there's no point in the anonymous type in the first place.
You can change your query to select myDataRow.Field<string>("Name") to get a set of strings.

You can use the keyword var to refer to anonymous types (which is what you're returning an IEnumerable<> of).
foreach(var row in distinctRows)
{
// do something with each anonymous type instance
}
Since you're only returning anonymous types with one string property however, you may as well project an IEnumerable<string>

That's because myDataRow["Name"] doesn't return a DataRow. Try
foreach(var item in distinctRows) {}

That's because the return value isn't a DataRow. It's an ad-hoc type that containes the property col1.

To build on SLaks answer . . .
var distinctRows = (from DataRow myDataRow in myDataTable.Rows
select new { col1 = myDataRow ["Name"]}).Distinct();
foreach(var row in distinctRows)
{
System.Console.Writeline(row.col1); //should work fine
}

The problem here is that you're selecting a new anonymous type by doing select new { col1 = myDataRow ["Name"]} and not the actual row itself. So when you try to iterate this as DataRow, it will error out because your anonymous type being selected is not of type DataRow.
If you want to be able to select a whole data row and not just the name field, you will need to implement a custom IEqualityComparer for data row to pass to the Distinct() extension method.
An example would be:
public class NameComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow left, DataRow right)
{
return left.Field<string>("Name").Equals(right.Field<string>("Name"));
}
public int GetHashCode(DataRow obj)
{
return obj.ToString().GetHashCode();
}
}
Then using it:
var distinctRows = (from DataRow myDataRow in myDataTable.Rows
select myDataRow).Distinct(new NameComparer());

You can also foreach them but first you had to List them as the following :
List<string> rslt =(from DataRow myDataRow in myDataTable.Rows
select new { col1 = myDataRow ["Name"].ToString()}).Distinct().ToList();
foreach(string str in rlst)
{}
Hope this helped

Related

Method Name Expected - VB.NET to C# Conversion [duplicate]

I have a DataTable with multiple rows. I'm using a foreach loop to loop through each item and return the name. This is returning the same (1st) value for each row. What have I done wrong?
DataTable table = new DataTable();
table.Columns.Add("tag", typeof(string));
string name = hfSelected.Value;
string[] names = name.Split(',');
for (int i = 0; i < names.Length; i++)
table.Rows.Add(new object[] { names[i] });
DataRow row = table.Rows[0];
foreach (var item in table.Rows)
{
Value = row["tag"].ToString() // this is returning the same value for both items in the table.
}
In a comment you mentioned that you get the error:
cannot apply indexing with [] to an expression of type object
when trying to access item["tag"] in the foreach loop.
You need to explicitly declare the DataRow in the foreach.
// declare DataRow here, not var
foreach (DataRow item in table.Rows)
{
// use item here
Value = item["tag"].ToString(); // use += to concatenate string
}
The reason is that the DataRowCollection implements a non-generic IEnumerable so you index an object instead of DataRow. The solution above casts to a DataRow.
I would recommend looking at the Field<T>() and AsEnumerable() methods from System.Data.DataSetExtensions. AsEnumerable() returns an IEnumerable<DataRow>. Field() provides strongly typed access to the values (ie it casts/converts the types for you).
Then you can do:
foreach (var item in table.AsEnumerable())
{
// item is a DataRow here
var myString = item.Field<string>("tag"); // gets string
// you can also do
var myInt = item.Field<int>("Id"); // gets int
var myDate = item.Field<DateTime?>("Date"); // gets nullable DateTime?
var myValue = item.Field<decimal>("Price"); // gets decimal
}
Carl is correct, this is producing the same output, because inside the iteration, you use the same row, all the time. You should use 'item', instead of 'row' there (you don't need 'row' at all).
The exception you receive is because you declared 'item' with a dynamic type, it's
foreach (var item in table.Rows)
You can try
foreach (DataRow item in table.Rows)
this way, you'll be able to get the column info.
your iteration seems to be using the same 'row' variable instead of the 'item' variable you defined in the foreach statement.

replace List.foreach to LINQ

I'm new to LINQ and doing some experiments with it.
Sorry if it is a duplicate but I cant seem to find proper guide (for me) to it
I want to replace this code :
DataTable table
List<string> header = new List<string>();
table.Columns.Cast<DataColumn>().ToList().ForEach(col => header.Add(col.ColumnName));
with something LINQ like:
var LINQheader = from mycol in table.Columns select mycol.ColumnName;
LINQheader.tolist();
but it doesn't even compile.
what I want Is not a one line solution but would like some logic to understand how construct it with more complicated environments (Like choosing many node in XML with some logic)
here is the original code
table.Columns.Cast<DataColumn>().ToList().ForEach(col => header.Add(col.ColumnName));
Why Cast used?
because it allows you to treat DataColumnCollection items as a DataColumn not an object.
Why ToList used?
becuase it converts your IEnumerable to List and allows you to call ForEach because this function is special method that exists in List class.
Why ForEach used?
because it allows you to do what you want for each element on the list (in your case it adds column name of each column to another list(header)).
Simplified version:
now assume you want to add column names to header where they starts with "Student"
you can write something like this
DataTable table = new DataTable();
List<string> header = new List<string>();
foreach (DataColumn col in table.Columns)
{
if (col.ColumnName.StartsWith("Id")) // you can remove this line if you want to add all of them
header.Add(col.ColumnName);
}
you can also use this
table.Columns.Cast<DataColumn>()
.ToList()
.ForEach(col =>
{
if (col.ColumnName.StartsWith("Id"))
header.Add(col.ColumnName)
});
or
var headers = table.Columns.Cast<DataColumn>()
.Where(col => col.ColumnName.StartsWith("Id"))
.Select(col => col.ColumnName);
header.AddRange(headers);
You can use Enumerable.Aggregate() for this:
var header = table.Columns.Cast<DataColumn>().Aggregate(new List<string>(), (list, col) => { list.Add(col.ColumnName); return list; });
In general, Linq allows for retrieval and transformation of sequences of data from data sources. What you want to do in this question is to iterate over a sequence and return an immediate result. That isn't the primary focus of Linq, but there are methods that perform tasks like this, including Aggregate(), Average(), Count(), Max() and so on.
var LINQheader = from mycol in table.column select mycol.ColumnName;
LINQheader.tolist();
This will not compile as there is no such property in DataTable as column, there is only Columns, and you have to use .Cast() method as they are not implementing right interface (see #Uriil's answer).
Try this:
var LINQheader = from mycol in table.Columns.Cast<DataColumn>()
select mycol.ColumnName;
LINQheader.tolist();
If you want to use wrap it in an extension method, you can do it like this:
public static IEnumerable<string> GetHeaderColumns (this DataTable dataTable)
{
if (dataTable == null || !dataTable.Columns.Any())
{
yield break;
}
foreach (var col in dataTable.Columns.Cast<DataColumn>())
{
yield return col.ColumnName;
}
}
static void Main(string[] args)
{
DataTable tbl = new DataTable();
tbl.Columns.Add("A");
tbl.Columns.Add("B");
var p = from DataColumn col in tbl.Columns select col.ColumnName;
foreach(string a in p)
{
Console.WriteLine(a);
}
}
Here little code example. If you want to be List<string>, use ToList().
EDIT:
Like #Grundy says you missing to specify type of the col, which is DataColumn.
List<string> columnList = (from DataColumn mycol in table.Columns select mycol.ColumnName).ToList();
Here this will be your one line.
Why not simple select like
DataTable table;
IEnumerable<string> header = from mycol in table.Columns.Cast<DataColumn>()
select mycol.ColumnName;
You have some problems, which requires workaround:
ForEach is List specifict method, so can can not translate it into LINQ
LINQ is for data selection, aggregation, but not for data
modification
table.Columns, returns DataColumnCollection, which does not
implement IEnumerable<T>, so you will have to cast it anyway:
var LINQheader = from mycol in table.Columns.Cast<DataColumn>()
select name.ColumnName;

Linq return type conversion

I have a linq query as follows,
var result =
from Record in DBContext.GetAll().
group new { Record.Filed1, Record.Field2} by Record.Field3
into newGroup
select new
{
BodyRegion = newGroup.Key,
ByScanner =
from exam in newGroup
group exam.MaxValue by exam.Model
into myGroup
select myGroup,
ByExam =
from exam in newGroup
group exam.MaxValue by exam.Protocol
into myGroup2
select myGroup2
};
Then I iterate throught them,
foreach (var rec in result)
{
foreach (var byScanner in rec.ByScanner)
{
ProcessResult(byScanner.Key, byScanner.ToList());
}
foreach (var byExam in rec.ByExam )
{
ProcessResult(byExam.Key, byExam.ToList());
}
}
Everything works fine.
But Iwant to move Linq query (first code snippet) to a function, what should be the return type the function?
Return type of a function can not be var. If I give IEnumerable< Object > then while iterating I can't access rec.ByScanner, rec.ByExam because Object doesn't contain them.
How to resolve this issue?
EDIT:
I tried by creating a new class and filling them in that. But Grouping attributes byScanner.Key, byScanner.ToList() are not accessible. How this can be solved?
You are using an Anonymous Type. These shouldn't be passed around methods.
One thing you can do is create (Let's call it 'Record') a class with properties BodyRegion, ByScanner and ByExam and pass IEnumerable<Record>.

Converting DataTable to Generic List<T>

I am trying to convert a Data table to a generic List. I am following this post. DataTable to List<object>.
Method is
public static List<MProps> TableToList<T1>(DataTable dt)
{
if (dt == null)
{
return null;
}
List<DataRow> rows = new List<DataRow>();
foreach (DataRow row in dt.Rows)
{
rows.Add(row);
}
return TableToList<T1>(rows);
}
I keep getting two errors at "return TableToList<T1>(rows);" Saying
Error 24 Argument 1: cannot convert from 'System.Collections.Generic.List<System.Data.DataRow>' to 'System.Data.DataTable' C:\Users..\Program.cs
AND
Error 23 The best overloaded method match for 'xx.Program.TableToList<T1>(System.Data.DataTable)' has some invalid arguments C:\Users\--\Program.cs
I cant figure out what is the problem.
Use
List<DataRow> rows = dt.AsEnumerable().ToList();
If you need to return List
return (from DataRow row in dt.Rows
select new MProps
{
//assign properties here
prop1 = row["colname"].ToString()
}).ToList();
In your return, you're trying to call the method again, but you're passing rows, which is a List<DataRow>. Your method expects a DataTable, so it's telling you that you're calling your method incorrectly.
Theoretically, you should be making a List<MProps> and returning that, rather than making a List<DataRow> and calling TableToList. Perhaps make a DataRowToMProp method and call that for every DataRow?
var lst = from x in dt.AsEnumerable()
where x.Field <string>("PersonName") == "ching" select x;

Problem in converting a generic list<string> or string[] array to datatable

I have the below function
public static DataTable ToTable<T>(this IEnumerable<T> listItem)
{
//Return null if the list is empty
if (listItem == null || listItem.Count() == 0) return null;
//Gets the type of the object
var listType = listItem.First().GetType();
//Initialize a new datatable
var dataTable = new DataTable(listType.Name);
//Create the datatable column names and types
listType.GetProperties().ToList().ForEach(col => dataTable.Columns.Add(col.Name, col.PropertyType));
//Get the datatable column names
var dataTableColumnNames = dataTable.GetDatatableColumnNames();
listItem.ToList().ForEach(item =>
{
//create a new datarow
var dataRow = dataTable.NewRow();
dataTableColumnNames
.Where(propName => listType.GetProperty(propName) != null)
.ToList()
.ForEach(columnName =>
//Exception happens here in the next line
dataRow[columnName] = listType.GetProperty(columnName).GetValue(item, null));
//Add the row to the data table
dataTable.Rows.Add(dataRow);
});
//Commit the changes to the datatable
dataTable.AcceptChanges();
return dataTable;
}
It works great for dictionary object and generic list as List<MyClass> .. but not for
List<string> or string[].
For those I am getting an exception as Parameter count mismatch.
The error is coming at
dataRow[columnName] = listType.GetProperty(columnName).GetValue(item, null));
What is the mistake that is happening?
Please help
Here's the deal. The index operator is actually considered a property when using reflection, hence parameter count mismatch.
If you break into your code and check the properties that are actually being enumerated by GetProperties(), you'll see the "Chars" property. That's the String's index operator. Since you didn't provide an index, you're getting a Parameter Count Mismatch error.
In essence, I assume string doesn't have any properties you want to put in your data table, but rather the string instance IS what you want to put in the data table.
You could create a model to store the string in, with the string as a property on the model, then the string would be stored with your current code. Otherwise, you will need to rethink your table generation algorithm for primitive types.
I hope this helps :)
Because one of the public properties of string is an indexer and you pass null as the index value. So you effectively end up doing this: string[null] which ends up in an exception.
I haven't verified this as I don't have VS available right now so I might be wrong but I'm pretty sure that's the problem.
Update: This question answers how you detect an indexed property: C# Reflection Indexed Properties

Categories

Resources