I am iterating on a lot of strings and I want to fill my first(and only) 3 Columns with each result and then start again in a new row. like:
A | B | C
------+--------+------
"DOG" | "CAT" | "FISH"
"FDF" | "AAA" | "RRR"
AND SO ON....
Basically after each row is "full" open new row.
HtmlNodeCollection tables = doc.DocumentNode.SelectNodes("//table");
HtmlNodeCollection rows = tables[2].SelectNodes(".//tr");
DataTable dataTable = new DataTable();
dataTable.Columns.Add("A", typeof(string));
dataTable.Columns.Add("B", typeof(string));
dataTable.Columns.Add("C", typeof(string))
try like this
for (int i = 0; i < rows .Count(); i++)
{
DataRow datarowObj= dataTable .NewRow();
datarowObj["A"] = yourValue;
datarowObj["B"] = yourValue;
datarowObj["C"] = yourValue;
dataTable.Rows.Add(datarowObj);
}
You could use Linq's GroupBy to split the long list into groups of 3:
sample-data:
DataTable table = new DataTable();
table.Columns.Add("Col1");
table.Columns.Add("Col2");
table.Columns.Add("Col3");
List<string> longList = Enumerable.Range(1, 99).Select(i => "row " + i).ToList();
group the long list into parts of three:
var groupsWithThree = longList
.Select((s, i) => new { Str = s, Index = i })
.GroupBy(x => x.Index / 3);
add them to the table:
foreach (var group3 in groupsWithThree)
table.Rows.Add(group3.First().Str, group3.ElementAt(1).Str, group3.Last().Str);
Note that it presumes that the list is divisible by three.
Manage with DataRoxw, for instance, after adding an empty DataRow to your DataTable :
DataRow row = table.Rows[0];
foreach (object item in row.ItemArray)
{
?
dataTable.Rows.Add(new object[] { "A1", "B1", "C1" })
// Alternatively
object[] arr = new object[] { "A2", "B2", "C2" };
dataTable.Rows.Add(arr);
Related
I have got this situation with a datatable like this
C1 C2 C3
A AA 4
BB 6
B CC 3
DD 3
EE 4
C FF 5
GG 5
and my output should be like this
C1 C2 C3
A AA,BB 10
B CC,DD,EE 10
C FF,GG 10
How can i group by the column with the space till the next value comes up
What i did was i took all the row itemarray and then using some string manipulation and regex got the row value as for the first two values like this and assigned to a variable in a query using Let
A,AA,BB,10|B,CC,DD,EE,10 but then i cannot add it using the
**DT.clone.rows.Add(x.split("|"c))* method as there its not incrementing and adding the whole joined string
Any other input where i can manipulate and add it (P.S i know linq is querying language)
Thank you for your time
You can use .GroupBy to get result needed
Here is your class:
public class Data
{
public string C1 { get; set; }
public string C2 { get; set; }
public int C3 { get; set; }
}
Imagine that you have list of Data objects, so your GroupBy expression will be following:
var result = list.GroupBy(g => g.C1, (a, b) => new {C1 = a, C2 = b.ToList()})
.Select(g => new
{
g.C1,
C2 = string.Join(",", g.C2.Select(m => m.C2)),
C3 = g.C2.Sum(m => m.C3)
})
.ToList();
A simple .GroupBy can give you expected result, Edited to handle Null or WhiteSpace Columns
var res = ListModel.Where(e => !string.IsNullOrWhiteSpace(e.C1)
&& !string.IsNullOrWhiteSpace(e.C2))
.GroupBy(e => e.C1).Select(e => new
{
e.Key,
c2 = string.Join(",", e.Select(x => x.C2).ToList()),
c3 = e.Sum(x => x.C3)
}).ToList();
Hello All first of all Thank you for your time and effort i Did this use case using this code
This gave me all row item array in string and than in the end with a little Split method i was able to add it to my datatable
String.Join("|",(System.Text.RegularExpressions.Regex.Replace(String.Join("|",(From roww In DT.AsEnumerable() Select String.Join(",",roww.ItemArray) ).ToList),"\|,",",")).Split("|"c).
Select(Function(q)CStr(q)+","+CStr(String.join("|",System.Text.RegularExpressions.Regex.Matches(CStr(q),"\d+").Cast(Of match)).Split("|"c).Sum(Function(r) CInt(r) ))).tolist),",\d+,",",")```
Try following code which is tested
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
dt1.Rows.Add(new object[] { "A", "AA", 4});
dt1.Rows.Add(new object[] { null, "BB", 6});
dt1.Rows.Add(new object[] { "B", "CC", 3});
dt1.Rows.Add(new object[] { null, "DD", 3});
dt1.Rows.Add(new object[] { null, "EE", 4});
dt1.Rows.Add(new object[] { "C", "FF", 5});
dt1.Rows.Add(new object[] { null, "GG", 5});
//replace nulls in column 1 with actual values
string previous = "";
foreach(DataRow row in dt1.AsEnumerable())
{
if (row.Field<string>("C1") == null)
{
row["C1"] = previous;
}
else
{
previous = row.Field<string>("C1");
}
}
DataTable dt2 = dt1.Clone();
var groups = dt1.AsEnumerable().GroupBy(x => x.Field<string>("C1")).ToList();
foreach (var group in groups)
{
dt2.Rows.Add(new object[] {
group.Key,
string.Join(",", group.Select(x => x.Field<string>("C2"))),
group.Select(x => x.Field<int>("C3")).Sum()
});
}
}
}
}
Yet another way using Skip, TakeWhile, and GroupBy extensions:
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
//The output table.
DataTable dt2 = dt1.Clone();
dt1.Rows.Add(new object[] { "A", "AA", 3 });
dt1.Rows.Add(new object[] { null, "BB", 6 });
dt1.Rows.Add(new object[] { "B", "CC", 3 });
dt1.Rows.Add(new object[] { null, "DD", 3 });
dt1.Rows.Add(new object[] { null, "EE", 4 });
dt1.Rows.Add(new object[] { "C", "FF", 5 });
dt1.Rows.Add(new object[] { null, "GG", 6 });
var rows = dt1.Rows.Cast<DataRow>().AsEnumerable();
foreach (var row in rows.Where(r => r.Field<string>("C1") != null))
{
var indx = dt1.Rows.IndexOf(row) + 1;
var q = rows
.Skip(indx)
.TakeWhile(t => t.Field<string>("C1") == null)
.GroupBy(g => g.Field<string>("C1"))
.Select(g => new
{
C1 = row.Field<string>("C1"),
C2 = $"{row.Field<string>("C2")}, {string.Join(", ", g.Select(s => s.Field<string>("C2")))}",
C3 = row.Field<int>("C3") + g.Sum(s => s.Field<int>("C3")),
}).FirstOrDefault();
if (q != null)
dt2.Rows.Add(q.C1, q.C2, q.C3);
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = dt2;
The idea behind this snippet is to:
Get the complete rows and iterate through them.
For each complete row, we get it's index from the original DataTable and add 1 to make a starting search point for the incomplete rows. The Skip extension is the method to achieve that.
The TakeWhile extension function gets the incomplete rows and stops at the next complete row.
The GroupBy extension function groups the incomplete rows to concatenate their C2 values and sum their C3 values, add the results to the values of the complete row and create a temporary anonymous object to hold these values.
Extract the anonymous object and add a new DataRow to the output DataTable.
And finally, bind the output DataTable to a DGV.
Happy 2020 for all.
I have Data Table with the following data
Number Type Order count
1 1 R 1
1 1 R 1
1 1 R 1
1 2 R 1
I am looking to get to this result
Number Type Order count
1 1 R 3
1 2 R 1
How can I group by three columns
var result = dt.AsEnumerable()
.GroupBy(x => {x.Field<string>("Number"))//need to group by Type and order also need to sum te total counts
rgoal
Your question made me curious, so I did some digging on Stack Overflow.
esc's answer appears will also solve your issue. It is posted under: How do I use SELECT GROUP BY in DataTable.Select(Expression)?:
Applying his method to your problem gave me this solution:
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
This will return a DataTable matching the schema of the input DataTable with the grouping and counts you requested.
Here is the full code I use to verify in LINQPad:
DataTable dt = new DataTable("Demo");
dt.Columns.AddRange
(
new DataColumn[]
{
new DataColumn ( "Number", typeof ( int ) ),
new DataColumn ( "Type", typeof ( int ) ),
new DataColumn ( "Order", typeof ( string ) ),
new DataColumn ( "Count", typeof ( int ) )
}
);
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,2,"R", 1 });
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
foreach (DataRow row in dt2.Rows)
{
for (int i = 0; i < dt2.Columns.Count; i++)
Console.Write("{0}{1}",
row[i], // Print column data
(i < dt2.Columns.Count - 1)? " " : Environment.NewLine); // Print column or row separator
}
Here are the results:
1 1 R 3
1 2 R 1
I have a datatable that looks like this:
Row Side Value
1 A 34.8
1 B 33.9
1 C 33.1
2 A 32.6
2 B 32.0
2 C 35.7
3 A 34.6
3 B 34.0
3 C 33.5
One thing I needed to do was compute the average of each Row which I did like:
var avg = (from row in dt.AsEnumerable()
group row by new { RowMeas = row.Field<string>("Row") } into grp
select new
{
RowMeas = grp.Key.RowMeas,
AVG = grp.Average(r => r.Field<double>("Value"))
}).ToList();
Now I need to do something similar but instead of just taking the average I want to use a formula for each row like 4*A + 3*B + 2*C
Can I do this using LINQ like above but instead of AVG somehow work this formula in? In other software we do this calculation manually by transposing the datatable so that there are A, B, C columns which can then be used in an formula on a new column. Since there's not an easy way to transpose in C# I'm hoping I can do this using LINQ.
var dt = new DataTable();
dt.Columns.Add("Row", typeof(string));
dt.Columns.Add("Side", typeof(string));
dt.Columns.Add("Value", typeof(double));
dt.Rows.Add(1, "A", 34.8);
dt.Rows.Add(1, "B", 33.9);
dt.Rows.Add(1, "C", 33.1);
dt.Rows.Add(2, "A", 32.6);
dt.Rows.Add(2, "B", 32.0);
dt.Rows.Add(2, "C", 35.7);
dt.Rows.Add(3, "A", 34.6);
dt.Rows.Add(3, "B", 34.0);
dt.Rows.Add(3, "C", 33.5);
var query = dt
.AsEnumerable()
.GroupBy(x => x.Field<string>("Row"))
.Select(x => new
{
Row = x.Key,
A = x.Where(y => y.Field<string>("Side") == "A").Select(z => z.Field<double>("Value")).FirstOrDefault(),
B = x.Where(y => y.Field<string>("Side") == "B").Select(z => z.Field<double>("Value")).FirstOrDefault(),
C = x.Where(y => y.Field<string>("Side") == "C").Select(z => z.Field<double>("Value")).FirstOrDefault()
})
.Select(x => new
{
Row = x.Row,
Result = 4 * x.A + 3 * x.B + 2 * x.C
})
;
foreach (var q in query)
Console.WriteLine("Row = {0}, Result = {1}", q.Row, q.Result);
Result in LinqPad.
What I m trying to do is relatively simple. I would like to use linq to compute some aggregated function on a group and then put the result back into a datatable of the same format. I did a lot of research and think I should use System.Data.DataSetExtensions and copy to datatable funtion. Here is my random datatable:
DataTable ADataTable = new DataTable("ADataTable");
// Fake table data
ADataTable.Columns.Add("PLANT", typeof(int));
ADataTable.Columns.Add("PDCATYPE_NAME", typeof(int));
ADataTable.Columns.Add("Month", typeof(int));
ADataTable.Columns.Add("Year", typeof(int));
ADataTable.Columns.Add("STATUS_NAME_REPORT", typeof(string));
ADataTable.Columns.Add("SAVINGS_PER_MONTH", typeof(double));
for (int i = 0; i < 15; i++)
{
for (int j = 1; j < 5; j++)
{
DataRow row = ADataTable.NewRow();
row["PLANT"] = j;
row["PDCATYPE_NAME"] = j;
row["Month"] = DateTime.Now.Month;
row["Year"] = DateTime.Now.Year;
row["STATUS_NAME_REPORT"] = "Report";
row["SAVINGS_PER_MONTH"] = j*i;
ADataTable.Rows.Add(row);
}
}
Now I will clone this format and do a simple sum on it via linq:
DataTable newtable = ADataTable.Clone();
// The actual query
IEnumerable<DataRow> query = (from rows in ADataTable.AsEnumerable()
group rows by new
{
PLANT = rows.Field<int>("PLANT"),
PDCATYPE_NAME = rows.Field<int>("PDCATYPE_NAME"),
Month = rows.Field<int>("Month"),
Year = rows.Field<int>("Year"),
STATUS_NAME_REPORT = rows.Field<string>("STATUS_NAME_REPORT")
} into g
select new
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
sum = g.Sum(savings => savings.Field<double>("SAVINGS_PER_MONTH")),
});
newtable = query.CopyToDataTable<DataRow>();
The LINQ works fine but as soon as I put IEnumarable DataRow in front I get error that I cannot convert anonymys type to datarow. But if I put select new datarow I get an error that fields are unknown...
How do I proceed please?
You have multiple options, First is to use reflection to create a DataTable based on IEnumerable<T> and the other options is to populate your DataTable by enumerating your query like:
var query = ADataTable.AsEnumerable()
.GroupBy(row => new
{
PLANT = row.Field<int>("PLANT"),
PDCATYPE_NAME = row.Field<int>("PDCATYPE_NAME"),
Month = row.Field<int>("Month"),
Year = row.Field<int>("Year"),
STATUS_NAME_REPORT = row.Field<string>("STATUS_NAME_REPORT")
});
foreach (var g in query)
{
newtable.LoadDataRow(new object[]
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
g.Sum(savings => savings.Field<double>("SAVINGS_PER_MONTH"))
}, LoadOption.OverwriteChanges);
}
The error in your code is because of selecting an anonymous type using select new and then trying to store it in IEnumerable<DataRow>. You can't specify DataRow in select as it is not accessible directly.
You may also see: How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow
This also works:
newtable2 = ADataTable.AsEnumerable().GroupBy(a => new
{
PLANT = a.Field<int>("PLANT"),
PDCATYPE_NAME = a.Field<int>("PDCATYPE_NAME"),
Month = a.Field<int>("Month"),
Year = a.Field<int>("Year"),
STATUS_NAME_REPORT = a.Field<string>("STATUS_NAME_REPORT")
}).Select(g =>
{
var row = newtable2.NewRow();
row.ItemArray = new object[]
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
g.Sum(r => r.Field<double>("SAVINGS_PER_MONTH"))
};
return row;
}).CopyToDataTable();
using System.Data.DataSetExtensions (Which requires a reference)
My Table1 has some Header and Values:
KundeID KundeName Produkt Comment
1 Michael Jogurt "nichts"
2 Raj "Ich bin cool"
3 Gary Fanta "yahoo"
4 Miky Sprite
I want to change to Table2, make Values from Produkt as Header Columns:
KundeID KundeName Comment Jogurt Fanta Sprite
1 Michael "nichts" x
2 Raj "Ich bin cool"
3 Gary "yahoo" x
4 Miky x
My code for Table1:
DataTable table = new DataTable("Kunde");
table.Columns.Add("KundeID", typeof(Int32));
table.Columns.Add("KundeName", typeof(String));
table.Columns.Add("Produkt", typeof(String));
DataTable comment = new DataTable("Comment");
comment.Columns.Add("KundeName", typeof(String));
comment.Columns.Add("Comment", typeof(String));
DataSet ds = new DataSet("DataSet");
ds.Tables.Add(table);
ds.Tables.Add(comment);
object[] o1 = { 1, "Michael", "Jogurt" };
object[] o2 = { 2, "Raj" };
object[] o3 = { 3, "Gary", "Fanta" };
object[] o4 = { 4, "Miky", "Sprite" };
object[] c1 = { "Raj", "Ich bin cool" };
object[] c2 = { "Gary", "yahoo" };
object[] c3 = { "Michael", "nichts" };
table.Rows.Add(o1);
table.Rows.Add(o2);
table.Rows.Add(o3);
table.Rows.Add(o4);
comment.Rows.Add(c1);
comment.Rows.Add(c2);
comment.Rows.Add(c3);
var results = from table1 in table.AsEnumerable()
join table2 in comment.AsEnumerable()
on table1.Field<string>("KundeName") equals table2.Field<string>("KundeName") into prodGroup
from table4 in prodGroup.DefaultIfEmpty()
select new
{
KundeID = table1.Field<Int32?>("KundeID"),
KundeName = table1.Field<String>("KundeName"),
Produkt = table1.Field<String>("Produkt"),
Comment = table4 != null ? table4.Field<String>("Comment") : null,
};
dataGridView1.DataSource = results.ToList();
How can I take Value from "Produkt" and make it Header? thank you guys for helping
This should do the trick regardless of how many different products come up. It's pretty short and concise.
// build the new data table
var result = new DataTable();
result.Columns.Add("KundeID", typeof(Int32));
result.Columns.Add("KundeName", typeof(String));
result.Columns.Add("Comment", typeof(String));
result.Columns.AddRange(
(from c in
(from r in table.AsEnumerable()
where !r.IsNull("Produkt") && !string.IsNullOrEmpty(r.Field<string>("Produkt"))
select r.Field<string>("Produkt")).Distinct() // added DISTINCT
select new DataColumn(c, typeof(bool))).ToArray()
);
foreach (var r in results)
{
var productIndex = result.Columns.IndexOf(r.Produkt);
var vals = new List<object>() { r.KundeID, r.KundeName, r.Comment };
for (int i = 3; i < result.Columns.Count; i++)
{
if (i == productIndex)
{
vals.Add(true);
}
else
{
vals.Add(false);
}
}
result.LoadDataRow(vals.ToArray(), true);
}
var products = table.AsEnumerable()
.GroupBy(c => c["Produkt"])
.Where(g => !(g.Key is DBNull))
.Select(g => (string)g.Key)
.ToList();
var newtable = table.Copy();
products.ForEach(p=>newtable.Columns.Add(p,typeof(bool)));
foreach (var row in newtable.AsEnumerable())
{
if (!(row["Produkt"] is DBNull)) row[(string)row["Produkt"]] = true;
}
newtable.Columns.Remove("Produkt");
Try following...this may help you....
DataTable table = new DataTable("Kunde");
table.Columns.Add("KundeID", typeof(Int32));
table.Columns.Add("KundeName", typeof(String));
table.Columns.Add("Produkt", typeof(String));
table.Columns.Add("Comment", typeof(String));
object[] o1 = { 1, "Michael", "Jogurt", "nichts" };
object[] o2 = { 2, "Raj","","Ich bin cool" };
object[] o3 = { 3, "Gary", "Fanta","yahoo" };
object[] o4 = { 4, "Miky", "Sprite","" };
table.Rows.Add(o1);
table.Rows.Add(o2);
table.Rows.Add(o3);
table.Rows.Add(o4);
Dictionary<int,string> dictObj=new Dictionary<int, string>();
for (int i = 0; i < table.Rows.Count; i++)
{
dictObj.Add(Convert.ToInt32(table.Rows[i][0].ToString()), table.Rows[i][2].ToString());
}
foreach (var obj in dictObj)
{
if (string.IsNullOrEmpty(obj.Value))
{
table.Columns.Add(obj.Value, typeof(String));
DataRow row = table.Rows[obj.Key];
row[obj.Value] = "X";
}
}