I have the following DataTable:
DataTable itemsOnSkid = new DataTable();
itemsOnSkid.Columns.Add("ItemNumber");
itemsOnSkid.Columns.Add("Qty");
And I need to aggregate this datatable by itemnumber. I'm using the following linq code:
var result = from row in itemsOnSkid.AsEnumerable()
group row by row.Field<string>("ItemNumber") into grp
select new
{
ItemNumber = grp.Key,
Qty = grp.Sum(r => r.Field<int>("Qty"))
};
Problem is that I need to replace the first datatable with this result, but I'm not able to use .CopyToDataTable() on result.
How can I convert this result back to a datatable?
You can't use CopyToDataTable() in this case, since the thing you're copying from has to be a DataRow. I think you're stuck doing it manually.
DataTable items = new DataTable();
items.Columns.Add("number");
items.Columns.Add("qty");
var result = from r in items.AsEnumerable()
group r by r.Field<string>("number") into grp
select new {
number = grp.Key,
qty = grp.Sum(r => r.Field<int>("qty"))
};
DataTable newItems = new DataTable();
newItems.Columns.Add("number");
newItems.Columns.Add("qty");
foreach (var item in result) {
DataRow newRow = newItems.NewRow();
newRow["number"] = item.number;
newRow["qty"] = item.qty;
newItems.AddRow(newRow);
}
I have tried fast the same as TFischer, but using directly a DataRow. No extra object.
This is my Code:
private static void Main(string[] args)
{
var itemsOnSkid = CreateDataTable();
FillData(itemsOnSkid);
var result = itemsOnSkid.AsEnumerable().GroupBy(row => row.Field<string>("ItemNumber")).Select(
grp =>
{
var newRow = itemsOnSkid.NewRow();
newRow["ItemNumber"] = grp.Key;
newRow["Qty"] = grp.Sum(r => r.Field<int>("Qty"));
return newRow;
}).CopyToDataTable();
}
private static DataTable CreateDataTable()
{
var itemsOnSkid = new DataTable();
itemsOnSkid.Columns.Add("ItemNumber");
itemsOnSkid.Columns.Add("Qty", typeof(int));
return itemsOnSkid;
}
// Fill some Data in the Table
private static void FillData(DataTable itemsOnSkid)
{
for (int i = 1; i <= 10; i++)
{
var newRow = itemsOnSkid.NewRow();
newRow["ItemNumber"] = i % 3;
newRow["Qty"] = i;
itemsOnSkid.Rows.Add(newRow);
}
}
I Hope it helps.
var query = from empl in te.Employees.AsEnumerable() select empl;
List<Employee> dt = query.ToList();
gdempdetails.DataSource = dt;
gdempdetails.DataBind();
Here's a simpler way to do this
// Create the table if you don't already have it
// Otherwise ignore this part
DataTable newItems = new DataTable();
newItems.Columns.Add("number");
newItems.Columns.Add("qty");
// LINQ query
IEnumerable<DataRow> result = from row in itemsOnSkid.AsEnumerable()
group row by row.Field<string>("ItemNumber") into grp
select newItems.LoadDataRow(new object[]
{
ItemNumber = grp.Key,
Qty = grp.Sum(r => r.Field<int>("Qty"))
}, false);
// Copy rows to DataTable
newItems = result.CopyToDataTable<DataRow>();
Related
I have a DataTable like this
ColA ColB
1 OneThree
2 FourTwo
3 EightNine
4 ThreeEightFive
5 SevenNine
and the substrings are in another array ("Two","Eight", "Three")
How do I filter the DataTable using ColB and get the rows which contains any one of the substrings in the array? Array might have n number of substrings.
Is there a possible way using Linq without looping thru each array element and checking the colB using CONTAINS keyword?
This will return all ColB values that contain values from your list.
var dt = // your datatable
var list = new List<string>{ "Two", "Eight", "Three" };
var matches = dt.AsEnumerable()
.Where(dr => list.Any(l => dr.Field<string>("ColB").Contains(l)))
.Select(r => r.Field<string>("ColB"))
.ToList();
Please note that Linq still performs loops behind the scenes. This may not be very efficient (due to the Where/Any), especially on large data sets.
If you want to return a DataTable use CopyToDataTable():
var matches = dt.AsEnumerable()
.Where(dr => list.Any(l => dr.Field<string>("ColB").Contains(l)))
.CopyToDataTable();
you can use LINQ as shown below to query datatable
DataTable dt = new DataTable();
dt.Columns.Add("ColA", typeof(int));
dt.Columns.Add("ColB", typeof(string));
dt.AcceptChanges();
var r1 = dt.NewRow();
r1["ColA"] = 1;
r1["ColB"] = "OneThree";
dt.Rows.Add(r1);
var r2 = dt.NewRow();
r2["ColA"] = 2;
r2["ColB"] = "FourTwo";
dt.Rows.Add(r2);
var r3 = dt.NewRow();
r3["ColA"] = 3;
r3["ColB"] = "EightNine";
dt.Rows.Add(r3);
var r4 = dt.NewRow();
r4["ColA"] = 4;
r4["ColB"] = "ThreeEightFive";
dt.Rows.Add(r4);
var r5 = dt.NewRow();
r5["ColA"] = 5;
r5["ColB"] = "SevenNine";
dt.Rows.Add(r5);
dt.AcceptChanges();
var subArray = new string[3] { "Two", "Eight", "Three" };
var query = from r in dt.AsEnumerable()
where subArray.Any(s=> r.Field<string>("ColB").IndexOf(s,StringComparison.InvariantCultureIgnoreCase)>-1)
select r.Field<string>("ColB");
foreach (var item in query)
{
Console.WriteLine(item);
}
If I well understand you need something like this.
Change MyDataTable with your filled DataTable and work on code following your necessities.
Dim containsMyWords As Func(Of String, Boolean) = Function(entryS As String)
Dim myWords() As String = {"r1", "r2", "r3", "r4", "r5", "r6"}
If myWords.Contains(entryS) Then Return True
Return False
End Function
Dim listRows = From dtR As DataRow In MyDataTable.AsEnumerable
Where containsMyWords(CType(dtR.Item("ColB"), String))
For Each mRow In listRows
Console.WriteLine(Join(mRow.ItemArray, "~"))
Next
I am writing a program that involves an PrintListView, I have gotten the Data to show, however each part of the Data is on a different Row. The Image marked as [1] below contains my PrintListView, which shows the Data on each different row, I want this Data to be on 1 row only. I am taking a DataSet, which I then turn into a DataTable. I then add the rows of each DataTable to a List, I have checked this in debug, and it only shows up with 3 values, which is great; but they are not on the same row. The Programming Language is C#.
https://i.stack.imgur.com/XAvEq.png [1]
If you could help, that would be handy.
Many Thanks.
public ObjectListView CreateOLVForSingleColumns(DataSet ds)
{
var olv = new ObjectListView();
olv.ShowGroups = false;
var colHeaders = new List<ColumnHeader>();
var objectList = new List<DataRow>();
//Setting the Column Names of the OLV
var totalOrders = new OLVColumn {AspectName = "Total_Orders", Text = "Total Orders", Width = 250};
var totalOrderValue = new OLVColumn {AspectName = "Total_Order_Value", Text = "Total Order Value", Width = 250};
var totalOrdersNotInvoiced = new OLVColumn {AspectName = "Total_Value_Of_Orders_Not_Invoiced", Text = "Total Order Value Not Invoiced", Width = 250};
colHeaders.AddRange(new ColumnHeader[] {totalOrders, totalOrderValue, totalOrdersNotInvoiced});
olv.Columns.AddRange(colHeaders.ToArray());
var dt = ds.Tables["Total Amount of Orders"];
var dt2 = ds.Tables["Total Value of Orders"];
var dt3 = ds.Tables["Total Value of Orders Not Invoiced"];
var dt4 = new DataTable();
dt4.Merge(dt);
dt4.Merge(dt2);
dt4.Merge(dt3);
for (int i = 1; i <= dt4.Rows.Count; i++)
{
var value = dt4.Rows[0];
var value2 = dt4.Rows[1];
var value3 = dt4.Rows[2];
olv.AddObjects(new DataRow[] { value, value2, value3 });
}
I think your problem is with DataTable.Merge not with ObjectListView.
I would suggest changing your data source so it all comes back in one table (e.g. by using a JOIN if your reading SQL). That said you can accomplish what you want with something like this:
var dt4 = new DataTable();
//add all the columns
foreach (DataColumn c in
dt.Columns.Cast<DataColumn>()
.Union(dt2.Columns.Cast<DataColumn>())
.Union(dt3.Columns.Cast<DataColumn>()))
{
dt4.Columns.Add(c.ColumnName, c.DataType);
}
if(dt.Rows.Count != dt2.Rows.Count|| dt.Rows.Count != dt3.Rows.Count)
throw new Exception("DataTables had different row counts");
for (int i = 0; i < dt.Rows.Count; i++)
{
var row = dt4.Rows.Add();
foreach (DataColumn c in dt.Columns)
if (dt.Rows[i][c] != DBNull.Value)
row[c.ColumnName] = dt.Rows[i][c];
foreach (DataColumn c in dt2.Columns)
if (dt2.Rows[i][c] != DBNull.Value)
row[c.ColumnName] = dt2.Rows[i][c];
foreach (DataColumn c in dt3.Columns)
if (dt3.Rows[i][c] != DBNull.Value)
row[c.ColumnName] = dt3.Rows[i][c];
}
If you run that code with the setup code:
var ds = new DataSet();
var dt = ds.Tables["Total Amount of Orders"] ?? new DataTable();
dt.Columns.Add("A");
dt.Rows.Add("A1");
dt.Rows.Add("A2");
var dt2 = ds.Tables["Total Value of Orders"]?? new DataTable();
dt2.Columns.Add("B");
dt2.Rows.Add("B1");
dt2.Rows.Add("B2");
var dt3 = ds.Tables["Total Value of Orders Not Invoiced"]?? new DataTable();
dt3.Columns.Add("C");
dt3.Rows.Add("C1");
dt3.Rows.Add("C2");
You get
If you use Merge then you get
I have a datatable where I need to take n number of columns. For ex: From the below datatable I need to take first 10 columns alone with data and put it in another datatable.
Code:
DataTable dtRecord = DAL.GetRecords();
I tried this and this doesn't take the required column.
var selectColumns = dtRecord .Columns.Cast<System.Data.DataColumn>().Take(10);
You can also use this
private DataTable GetNColumnsFromDataTable(DataTable tblSource, int outputCols)
{
DataTable columnOutput = tblSource.Copy();
if (outputCols > 0 && outputCols < tblSource.Columns.Count)
{
while (outputCols < columnOutput.Columns.Count)
{
columnOutput.Columns.RemoveAt(columnOutput.Columns.Count - 1);
}
}
return columnOutput;
}
You can do it like this:
var selectColumns = dtRecord.Columns.Cast<DataColumn>().Take(10);
var dtResult = new DataTable();
foreach (var column in selectColumns)
dtResult.Columns.Add(column.ColumnName);
foreach (DataRow row in dtRecord.Rows)
dtResult.Rows.Add(row.ItemArray.Take(10).ToArray());
Perhaps you should create a column of the same type and with the same expression:
dtResult.Columns.Add(column.ColumnName, column.DataType, column.Expression);
To copy from one DataTable to another, you can extract the columns of interest
var moveCols = dtRecord.Columns.Cast<DataColumn>().Take(10).Select(c => c.ColumnName).ToArray();
Then you must create new DataColumns in a new table, then create new DataRows in the new table:
var newTable = new DataTable();
foreach (var c in moveCols)
newTable.Columns.Add(c);
foreach (var r in dtRecord.AsEnumerable())
newTable.Rows.Add(moveCols.Select(c => r[c]).ToArray());
Which you can make an extension method on DataTable:
public static DataTable Slice(this DataTable dt, params string[] colnames) {
var newTable = new DataTable();
foreach (var c in colnames)
newTable.Columns.Add(c, dt.Columns[c].DataType);
foreach (var r in dt.AsEnumerable())
newTable.Rows.Add(colnames.Select(c => r[c]).ToArray());
return newTable;
}
Now you can call
var newTable = dtRecord.Slice(moveCols);
With a nice extension method, you can convert from Dictionarys to a DataTable dynamically:
var newTable = dtRecord.AsEnumerable().Select(r => moveCols.ToDictionary(c => c, c => r[c])).AsDataTable();
I have some for converting ExpandoObject and anonymous objects as well, as well as an extension to convert those to anonymous objects dynamically. Here is the code for Dictionarys to DataTable:
public static DataTable AsDataTable(this IEnumerable<IDictionary<string, object>> rows) {
var dt = new DataTable();
if (rows.Count() > 0) {
foreach (var kv in rows.First())
dt.Columns.Add(kv.Key, kv.Value.GetType());
foreach (var r in rows)
dt.Rows.Add(r.Values.ToArray());
}
return dt;
}
public static DataTable AsDataTable(this IEnumerable<Dictionary<string, object>> rows) => ((IEnumerable<IDictionary<string, object>>)rows).AsDataTable();
Get 10 columns:
var tbl = new System.Data.DataTable();
var cols = tbl.Columns.Cast<System.Data.DataColumn>().Take(10);
// if you wish to get first 10 columns...
If you want to get the data, then you have to loop through the columns to get the data.
var data = cols.SelectMany(x => tbl.Rows.Cast().Take(10).Select(y => y[x]));
of course, this will dump all the data into an ienumerable, if you want to use strong typed object or a list of one dimensional array, believe it's fairly simple, for example:
var data2 = cols.Select(x => tbl.Rows.Cast().Take(10).Select(y => y[x]).ToArray());
I have DataTable object (OutputDT1), I want to use LINQ to group by column ConfirmedBy, then convert it back to a DataTable object which has only two columns ConfirmBy and Count.
var result = from row in OutputDT1.AsEnumerable()
group row by new
{
ConfirmedBy = row.Field<string>("ConfirmedBy")
}
into grp
select new
{
ConfirmedBy = grp.Key.ConfirmedBy,
Count = grp.Count(),
};
A simple way would be:
DataTable dt = new DataTable();
foreach(var item in result)
{
dt.Rows.Add(item.ConfirmedBy, item.count);
}
Using the solution from How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a DataRow
we can write:
var result = (from row in OutputDT1.AsEnumerable()
group row by row.Field<string>("ConfirmedBy") into grp
select new
{
ConfirmedBy = grp.Key,
Count = grp.Count(),
}).CopyToDataTable();
I am using .NET 3.5 and need to convert the below select new result into a DataTable. Is there something built in for this or anyone know of a method that can do this?
var contentList = (from item in this.GetData().Cast<IContent>()
select new
{
Title = item.GetMetaData("Title"),
Street = item.GetMetaData("Street"),
City = item.GetMetaData("City"),
Country = item.GetMetaData("Country")
});
Easy and straightforward thing to do is to use reflection:
var records = (from item in this.GetData().Cast<IContent>()
select new
{
Title = "1",
Street = "2",
City = "3",
Country = "4"
});
var firstRecord = records.First();
if (firstRecord == null)
return;
var infos = firstRecord.GetType().GetProperties();
DataTable table = new DataTable();
foreach (var info in infos) {
DataColumn column = new DataColumn(info.Name, info.PropertyType);
table.Columns.Add(column);
}
foreach (var record in records) {
DataRow row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
row[i] = infos[i].GetValue(record);
table.Rows.Add(row);
}
Code may not be working up front but should give you a general idea. First, you get propertyInfos from anonymous type and use this metadata to create datatable schema (fill columns). Then you use those infos to get values from every object.
Here is one generic solution without Reflecting over the properties. Have an extension method as below
public static DataTable ConvertToDataTable<TSource>(this IEnumerable<TSource>
records, params Expression<Func<TSource, object>>[] columns)
{
var firstRecord = records.First();
if (firstRecord == null)
return null;
DataTable table = new DataTable();
List<Func<TSource, object>> functions = new List<Func<TSource, object>>();
foreach (var col in columns)
{
DataColumn column = new DataColumn();
column.Caption = (col.Body as MemberExpression).Member.Name;
var function = col.Compile();
column.DataType = function(firstRecord).GetType();
functions.Add(function);
table.Columns.Add(column);
}
foreach (var record in records)
{
DataRow row = table.NewRow();
int i = 0;
foreach (var function in functions)
{
row[i++] = function((record));
}
table.Rows.Add(row);
}
return table;
}
And Invoke the same using where parameters will be the column name in the order you want.
var table = records.ConvertToDataTable(
item => item.Title,
item => item.Street,
item => item.City
);
You can convert your list result to datatable by the below function
public static DataTable ToDataTable<T>(IEnumerable<T> values)
{
DataTable table = new DataTable();
foreach (T value in values)
{
if (table.Columns.Count == 0)
{
foreach (var p in value.GetType().GetProperties())
{
table.Columns.Add(p.Name);
}
}
DataRow dr = table.NewRow();
foreach (var p in value.GetType().GetProperties())
{
dr[p.Name] = p.GetValue(value, null) + "";
}
table.Rows.Add(dr);
}
return table;
}
There is a CopyToDataTable extension method which does that for you. It lives in System.Data.DataSetExtensions.dll
Try this:
// Create your datatable.
DataTable dt = new DataTable();
dt.Columns.Add("Title", typeof(string));
dt.Columns.Add("Street", typeof(double));
// get a list of object arrays corresponding
// to the objects listed in the columns
// in the datatable above.
var result = from item in in this.GetData().Cast<IContent>()
select dt.LoadDataRow(
new object[] { Title = item.GetMetaData("Title"),
Street = item.GetMetaData("Street"),
},
false);
// the end result will be a set of DataRow objects that have been
// loaded into the DataTable.
Original Article for code sample : Converting Anonymous type generated by LINQ to a DataTable type
EDIT: Generic Pseudocode:
void LinqToDatatable(string[] columns, Type[] datatypes, linqSource)
{
for loop
{
dt.columns.add(columns[i], datatypes[i]);
}
//Still thinking how to make this generic..
var result = from item in in this.GetData().Cast<IContent>()
select dt.LoadDataRow(
new object[] { string[0] = item.GetMetaData[string[0]],
string[1] = item.GetMetaData[srring[1]
},
false);
}
public static DataTable ListToDataTable<T>(this IList<T> data)
{
DataTable dt = new DataTable();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
dt.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T t in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(t);
}
dt.Rows.Add(values);
}
return dt;
}
After you do your select new you can to .ToList().ListToDataTable(). This uses ComponentModel reflection and is (theroetically) faster than System.Reflection.