My DataTable looks something like this:
id1
id2
id3
1
2
1
1
2
3
1
2
1
1
1
3
3
3
2
3
2
I want my program to remove all "duplicate combinations", so the table would look like this:
id1
id2
id3
1
2
1
1
1
3
3
3
2
3
2
Basically I want it to check if other rows are already contained within other rows, while treating the empty cells as a cell that could have any value at all.
I have tried working with the DataTable.DefaultView.ToTable(true) distinct method and adding column names as parameters but couldn't get it to do exactly what i want.
Also the size of the table is dynamic - There can be an infinite amount of columns.
I finally found a good solution. The distinct doesn't work (using IComarable because it leaves the row with the null instead of the row with values. I had to add a sort so the row with null is before the one with values. Then I delete the first row leaving the second row with values. Also found I had to add dt.AcceptChanges(); because the row did get deleted.
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("id1", typeof(int));
dt.Columns.Add("id2", typeof(int));
dt.Columns.Add("id3", typeof(int));
dt.Columns["id1"].AllowDBNull = true;
dt.Columns["id2"].AllowDBNull = true;
dt.Columns["id3"].AllowDBNull = true;
dt.Rows.Add(new object[] { 1});
dt.Rows.Add(new object[] { 2 });
dt.Rows.Add(new object[] { 1, 1 });
dt.Rows.Add(new object[] { 2, 3 });
dt.Rows.Add(new object[] { 1, 2, 1 });
dt.Rows.Add(new object[] { 1, 1, 3 });
dt.Rows.Add(new object[] { 3, 3 });
dt.Rows.Add(new object[] { 2, 3, 2 });
dt = RemoveDuplicates(dt);
}
static DataTable RemoveDuplicates(DataTable dt)
{
for (int col = dt.Columns.Count - 1; col >= 0; col--)
{
dt = dt.AsEnumerable().OrderBy(x => x.Field<int?>(col)).CopyToDataTable(); ;
}
for (int i = dt.Rows.Count - 2; i >= 0; i--)
{
bool delete = true;
for (int col = 0; col <= dt.Columns.Count - 1; col++ )
{
if (dt.Rows[i][col] == DBNull.Value)
continue;
if((int)dt.Rows[i][col] != (int)dt.Rows[i + 1][col])
{
delete = false;
break;
}
}
if (delete)
{
dt.Rows[i].Delete();
dt.AcceptChanges();
}
}
return dt;
}
Related
So here is the issue I am facing. I have a one to many list relationship.
Here are examples of how the lists would look.
The objects in list 1 are dynamic and have a few properties with the most important one being "id".
The second list has a defined object looking something like this "id" "value desc" "actual value".
The second list can have many rows belonging to the first list. The "value desc" is the property name and the "actual value".
I need to combine these lists into something that looks like this. An object with all list 1 properties and all corresponding rows in the second list. If the second list had 3 items belonging to an item in list 1 then the new object should have all properties of list 1 along with all the rows gathered from list 2 in a flat structure like way.
Data examples
Table 1:
id
name
1
bob
2
joe
Table 2
id
propname
value
1
length
2
1
age
12
1
haircolor
blue
2
length
5
2
age
90
2
haircolor
red
How I want the data to look
id
name
length
haircolor
age
1
bob
2
blue
12
2
joe
5
red
90
Currently, I have this working.
public IEnumerable<dynamic> test(List<dynamic> data, List<modal>
dataset)//closest
{
var query = (from a in data
join b in dataset
on a.id equals b.id into t
select new {
a,
t
});
return query;
}
However, the result is an object with properties of list 1 and then a property on that object that is an array of items found in list 2. I need these items to not be in an array and be property names with values on the new created object.
I hope my explanation was clear enough!
Use a pivolt table :
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("id", typeof(int));
dt1.Columns.Add("name", typeof(string));
dt1.Rows.Add(new object[] { 1, "bob" });
dt1.Rows.Add(new object[] { 2, "bob" });
DataTable dt2 = new DataTable();
dt2.Columns.Add("id", typeof(int));
dt2.Columns.Add("propname", typeof(string));
dt2.Columns.Add("value", typeof(object));
dt2.Rows.Add(new object[] { 1, "length", 2 });
dt2.Rows.Add(new object[] { 1, "age", 12 });
dt2.Rows.Add(new object[] { 1, "haircolor", "blue"});
dt2.Rows.Add(new object[] { 2, "length", 5 });
dt2.Rows.Add(new object[] { 2, "age", 90 });
dt2.Rows.Add(new object[] { 2, "haircolor", "red" });
DataTable pivot = new DataTable();
string[] properties = dt2.AsEnumerable().Select(x => x.Field<string>("propname")).Distinct().OrderBy(x => x).ToArray();
pivot.Columns.Add("id", typeof(int));
pivot.Columns.Add("name", typeof(string));
DataColumn[] columns = properties.Select(x => new DataColumn(x, typeof(object))).ToArray();
pivot.Columns.AddRange(columns);
var joins = from id1 in dt1.AsEnumerable()
join id2 in dt2.AsEnumerable() on id1.Field<int>("id") equals id2.Field<int>("id")
select new { id = id1.Field<int>("id"), name = id1.Field<string>("name"), id2 = id2 };
var groups = joins.GroupBy(x => x.id);
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["id"] = group.Key;
newRow["name"] = group.First().name;
foreach (var row in group)
{
newRow[row.id2.Field<string>("propname")] = row.id2.Field<object>("value");
}
}
}
}
}
I have a DataGridView that is being filled with DataTable → Fill() method.
I want to add another Table in the same DataGridView, in the next column, just when the other one ends. Is it possible?
Now I have something like this
da.Fill(dt);
dataGridView1.DataSource = null;
dataGridView1.Rows.Clear();
dataGridView1.DataSource = dt;
I want to add another DataTable to the same DataGridView in new columns. Thanks
Use the Merge function of DataTable and then shift elements up. See codes below:
private void AddTwoDataTable()
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("ID1", typeof(int));
dt1.Columns.Add("Value1", typeof(string));
dt1.Columns.Add("Value2", typeof(string));
DataTable dt2 = new DataTable();
dt2.Columns.Add("ID2", typeof(int));
dt2.Columns.Add("Value3", typeof(string));
dt1.Rows.Add(new object[] { 1, "a", "ab"});
dt1.Rows.Add(new object[] { 2, "b", "bc" });
dt1.Rows.Add(new object[] { 3, "c", "cd" });
dt1.Rows.Add(new object[] { 4, "d", "de" });
dt2.Rows.Add(new object[] { 101, "x" });
dt2.Rows.Add(new object[] { 102, "y" });
dt2.Rows.Add(new object[] { 103, "y" });
var newtable = MergetwoTables( dt1, dt2 );
dataGridView1.DataSource = newtable;
}
public static DataTable MergetwoTables(DataTable dt1, DataTable dt2)
{
DataTable table = new DataTable("NewTable");
table.Merge(dt1);
table.Merge(dt2);
int maxRows1 = dt1.Rows.Count;
int maxColu1 = dt1.Columns.Count;
int maxRows2 = dt2.Rows.Count;
int maxColu2 = dt2.Columns.Count;
// copy elements from new rows
for (int r = maxRows1; r < maxRows1 + maxRows2; r++)
for (int c = maxColu1; c < maxColu1 + maxColu2; c++)
table.Rows[r - maxRows1][c] = table.Rows[r][c];
//delete new rows
var maxrows = maxRows1 > maxRows2 ? maxRows1 : maxRows2;
for (int r = maxRows1 + maxRows2 - 1; r >= maxrows; r--)
table.Rows[r].Delete();
return table;
}
Output:
I am reading an excel using .NET and need to perform certain checks on that excel.
The excel example is as below:
ColumnA ColumnB ColumnC
1001 Null 10
1001 W101 5
1001 W102 4
1001 W103 2
1002 Null 12
1002 W104 5
1002 W105 3
1003 W106 5
1003 W107 2
The requirement is as follows:
If there is a Null entry in ColumnB, then I need to compare the values under ColumnC,i.e. the sum of the values(5,4,2 as against the values W101, W102, W103) should be equal to 10(value against Null), if not then write an error in a log file.
My problem is that the there can be n values in ColumnC, how to loop against it.
In the above excel, the values corresponding to 1001 (ColumnA) are 4 in number whereas, the values corresponding to 1002, 1003 (ColumnA) are 3 and 2 in number respectively.
How to write a generic logic for it, I am not able to understand.
Below is the code which I have written, but it will work only when I have 4 values corresponding to 1001 in ColumnA.
FileStream file = File.Open(path, FileMode.Open, FileAccess.Read);
IExcelDataReader obj = ExcelReaderFactory.CreateOpenXmlReader(file);//uses a 3rd party library
obj.IsFirstRowAsColumnNames = true;
DataSet ds = obj.AsDataSet();
DataTable dt = ds.Tables[0];
for (int i = 0; i < dt.Rows.Count; i++)
{
if (dt.Rows[i][1].ToString() == "Null")
{
double a = (double)dt.Rows[i][2];
double x = (double)dt.Rows[i + 1][2];
double y = (double)dt.Rows[i + 2][2];
double z = (double)dt.Rows[i + 3][2];
if (a != (x+ y + z))
{
Response.Write("Mismatch in row: " + dt.Rows[i + 1][1]);
Response.Write("<br/>");
}
}
}
Also, I have used a 3rd party library to read the excel and convert it into a DataSet.
The following query will get the results you need:
Test data used:
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("A", typeof(string)));
dt.Columns.Add(new DataColumn("B", typeof(string)));
dt.Columns.Add(new DataColumn("C", typeof(int)));
dt.Rows.Add(new object[] { "1001", "Null", 10 });
dt.Rows.Add(new object[] { "1001", "W101", 5 });
dt.Rows.Add(new object[] { "1001", "W102", 4 });
dt.Rows.Add(new object[] { "1001", "W103", 1 });
dt.Rows.Add(new object[] { "1002", "Null", 12 });
dt.Rows.Add(new object[] { "1002", "W104", 5 });
dt.Rows.Add(new object[] { "1002", "W105", 3 });
dt.Rows.Add(new object[] { "1003", "W106", 5 });
dt.Rows.Add(new object[] { "1003", "W107", 2 });
LINQ:
var result =
dt
.AsEnumerable()
// Group by the A-column.
.GroupBy(r => (string)r["A"])
// Get only those groups where the first item in the B-column is 'Null':
.Where(g => g.FirstOrDefault(r => (string)r["B"] == "Null") != null)
// Get only those groups where the sum of the items of the C-column
// after the first one is different from the first item:
.Where(g => (int)g.First()["C"] != g.Skip(1).Sum(r => (int)r["C"]))
.ToList();
The result will contain rows groupped by the A-column that have invalid sums. If you are not interested in the actaul rows but only if they are valid then put .Any() in place of .ToList().
If you don't use a "Null" strings but a real null like:
dt.Rows.Add(new object[] { "1001", null, 10 });
you'll need to exchange the first Where to:
.Where(g => g.FirstOrDefault(r => r.Field<string>("B") == null) != null)
I have a datatable in which multiple data is populated. Need to check a column which contains same values in different rows and add up all the similar rows into one in the same datatable.
for example
id pid pname pAmountex vat vat
1 4 t1 123 2
2 3 t2 45 3
3 4 t3 56 7
4 3 t4 23 8
in the above table,pid column has similar values 4& 3 .i need to sum up the pamountex,vat column of 1st and 3 rd rows for pid 4 and sum up 2 and 3 rows for pid 3.
Here is an example with LINQ:
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("pid");
dt.Columns.Add("pname");
dt.Columns.Add("pamountex",typeof(int));
dt.Columns.Add("vat",typeof(int));
dt.Rows.Add( new object[] { 1, 4, "t1", 123, 2 } );
dt.Rows.Add( new object[] { 2, 3, "t2", 45, 3 } );
dt.Rows.Add( new object[] { 3, 4, "t3", 56, 7 } );
dt.Rows.Add( new object[] { 4, 3, "t4", 23, 8 } );
dt.AsEnumerable()
.GroupBy(r => r.Field<string>("pid") )
.Where (r => r.Count() > 1)
.Select (gr =>
new { id = 0,
pid = gr.Key,
pname = "???",
pamountex = gr.Sum (g => g.Field<int>("pamountex")),
vat = gr.Sum (g => g.Field<int>("vat"))
})
.ToList()
.ForEach( r => dt.Rows.Add(
new object[] {
r.id,
r.pid,
r.pname,
r.pamountex,
r.vat } ));
You did not specify what values are required in id and pname columns for rows that keep sum so I added some defaults(id = 0 and pname = "???") - change it according to your needs.
I assumed that rows with source data should stay in the table.
I'm digging in in my Microsoft Visual Studio Documentation and I found this article under C# Reference (ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_csref/html/df01e266-5781-4aaa-80c4-67cf28ea093f.htm), It's about Interface Interface. Here's the example code:
class SelectSample1
{
static void Main()
{
//Create the data source
List<int> Scores = new List<int>() { 97, 92, 81, 60 };
// Create the query.
IEnumerable<int> queryHighScores =
from score in Scores
where score > 80
select score;
// Execute the query.
foreach (int i in queryHighScores)
{
Console.Write(i + " ");
}
}
}
//Output: 97 92 81
Instead of a List, is it also possible to query a DataTable and set the result of the query as the DataSource of a DataGridView?
If yes, suppose I have this structure:
Fruit | CategoryID
---------------------------------------
Lemon | 1
Orange | 1
Apple | 2
Pear | 2
Can anyone please give me an example (if possible, for a beginner's approach.. :). What I want is to display the result in a DataGridView. Display all fruits where its CategoryID is equal to 1. Please help,
Thanks in advance guys.
You need to use AsEnumerable() extension of Databe to select the rows and bind to DataGridView like this:
DataTable table = new DataTable();
table.Columns.Add("Fruit");
table.Columns.Add("ID", typeof(int));
table.Rows.Add(new object[] { "Lemon", 1 });
table.Rows.Add(new object[] { "Orange", 1 });
table.Rows.Add(new object[] { "Apple", 2 });
table.Rows.Add(new object[] { "Pear", 2 });
BindingSource bs = new BindingSource();
bs.DataSource = from row in table.AsEnumerable()
where row.Field<int>("ID") == 1
select new {Fruit = row.Field<string>("Fruit"), ID = row.Field<int>("ID")};
dataGridView1.DataSource = bs;
Try this
var results = from row in dataTable.AsEnumerable()
where row.Field<int>("CategoryID") == 1
select row ;
and you can bind the result row easily to your control.