Table doesn't have primary key c# [duplicate] - c#

This question already has answers here:
Best way to remove duplicate entries from a data table
(11 answers)
Closed 9 years ago.
Im trying to delete rows from DataTable AllItems with rows from DataTables Items; The purpose of this to get items from DataTable AllItems which are not inside DataTable Items
All these rows Fiiled from same Excel file which contains several columns and are equal.
I have tried using foreach loop:
foreach(DataRow dr in AllItems.Rows)
{
if (Items.Contains(dr))
{
AllItems.Rows.Remove(dr);
}
But I get following error: Table doesn't have primary key.
Does anyone knows how i can delete these rows?

You have a few choices here:
1. Add a Primary Key
you can add a primary key to your data table when creating it.
Assuming you had a column called "Id" then you would do it this way:
AllItems.PrimaryKey = new DataColumn[] { workTable.Columns["Id"] };}
Or, for cases where your primary key is a composite key (multiple columns):
AllItems.PrimaryKey = new DataColumn[] {
workTable.Columns["Id"],
workTable.Columns["Name"] };}
This would then allow Contains to work correctly.
2. Use a DataView
You can use a DataView to filter out the distinct rows;
DataView view = new DataView(AllItems);
DataTable distinctValues = view.ToTable(true, "Column1", "Column2" , ..., "ColumnN");
3. Find Matching Rows using Select
Or you can rely on the Select method to test if a corresponding row exists in the Items DataTable based on a statement that's like a SQL WHEREclause:
List<DataRow> rowsToRemove = new List<DataRow>();
foreach(DataRow allItemRow in AllItems.Rows)
{
if(Items.Select(String.Format("Id = {0}"),
allItemRow.Field<Int32>("Id")).Length == 0)
{
rowsToRemove.Add(allItemRow);
}
}
rowsToRemove.ForEach(x => x.Delete());
AllItems.AcceptChanges();
Note that it's important NOT to remove rows while you are iterating the collection of Rows in AllItems - instead, collect these rows, and remove them afterwards.
4. Filter on the way in
Also note, and I haven't tried it, but, depending on how you are selecting the rows out of Excel, you may be able to use the SQL DISTINCT clause; if you are using ODBC to load data from Excel then you could try filtering at source.

You may try this:
var exceptItems = AllItems.Rows.Cast<DataRow>()
.Except(Items.Rows.Cast<DataRow>(), DataRowComparer.Default)
.ToList();
As an alternative, if you want to keep working with the allItems data table after removing the items rows from it, you may try this (assuming that you have the column Id in both data tables, which uniquely identifies a row per data table):
var exceptItems = AllItems.Rows.Cast<DataRow>()
.Select((i, index) => new { id = i["Id"], index })
.Intersect(Items.Rows.Cast<DataRow>()
.Select((i, index) => new { id = i["Id"], index }))
.ToList();
for (int i = exceptItems.Count()-1; i >= 0; i--)
{
AllItems.Rows.RemoveAt(exceptItems[i].index);
}
Here's a nicer arrangement of the last example above:
AllItems.Rows.Cast<DataRow>()
.Select((i, index) => new { id = i["Id"], index })
.Intersect(Items.Rows.Cast<DataRow>()
.Select((i, index) => new { id = i["Id"], index }))
.OrderByDescending(i => i.index)
.ToList()
.ForEach(i => AllItems.Rows.RemoveAt(i.index));

Related

Access only rowData from DataTable [duplicate]

This question already has answers here:
How to 'foreach' a column in a DataTable using C#?
(7 answers)
Closed 6 years ago.
I have a DataTable which contains column names and rows. Now as per my requirement I have to get only row value without giving the column names as I have done now.Here is the code.
data = employees.AsEnumerable().Select(row=> new List<string>
{
row.Field<string>("EmployeeName"),
row.Field<string>("Company")
})
In the above code employees is DataTable. How to do it.I have to get the row values into data variable as shown in code.
Update
data = employees.AsEnumerable().Select(row=> new List<string>
{
foreach(DataRow row in employees.Rows)
{
foreach(DataColumn col in employees.Columns)
data.Add(row[col.Ordinal].ToString());
}
})
You could simply use this syntax
data = employees.AsEnumerable().Select(row=> new List<string>
{
row[0].ToString(),
row[1].ToString()
});
Where 0 is the index of the column EmployeeName and 1 is the index of the column Company (if your query is something like SELECT EmployeeName, Company from ....)
But, in my opinion, this is really a step backwards. Using column names preserves your code from the order in which the columns are loaded from the database table.
EDIT
If you want to loop over every row and for every row on every column you could use this code (at this point there is no much sense in using IEnumerable extensions)
With foreach:
foreach(DataRow row in employees.Rows)
foreach(DataColumn col in employees.Columns)
data.Add(row[col.ColumnName].ToString());
// or
// data.Add(row[col.Ordinal].ToString());
With standard loop
for(int r = 0; r < employees.Rows.Count; r++)
for(int c = 0; c < employees.Columns.Count; c++)
data.Add(employees.Rows[r][c].ToString());
maybe you can use that?
var dt = new DataTable();
dt.AsEnumerable().Select(row => new List<string>
{
row.Field<string>(0),
row.Field<string>(1)
});

C# Find non matching values in DataTables

I'm struggling with the following problem:
There are 2 DataTables (SSFE and FE in my case).
FE will contain items that match with SSFE, but it will also contain values not present in SSFE.
For Example
SSFE 1,2,3,4,5,6,9,10
FE 1,2,3,4,5,6,7,8,9,10,11
The ouput I need is in this example : 7, 8, 11.
I'm using the following code to find items that do match:
DataSet set = new DataSet();
//wrap the tables in a DataSet.
set.Tables.Add(SSFEData);
set.Tables.Add(FEData);
//Creates a ForeignKey like Join between two tables.
//Table1 will be the parent. Table2 will be the child.
DataRelation relation = new DataRelation("IdJoin", SSFEData.Columns[0], FEData.Columns[0], false);
//Have the DataSet perform the join.
set.Relations.Add(relation);
//Loop through table1 without using LINQ.
for (int i = 0; i < SSFEData.Rows.Count; i++)
{
//If any rows in Table2 have the same Id as the current row in Table1
if (SSFEData.Rows[i].GetChildRows(relation).Length > 0)
{
SSFEData.Rows[i]["PackageError"] = SSFEData.Rows[i].GetChildRows(relation)[0][1];
SSFEData.Rows[i]["SaleError"] = SSFEData.Rows[i].GetChildRows(relation)[0][2];
}
}
There should be an trick to find these items that do not have an relation.
Any suggestion will be great!
Well, you could of course use a little bit of LINQ by turning the data tables into IEnumerables using the AsEnumerable()1 extension method.
I am using a few assumptions to illustrate this:
"id" is the column with an integer value relating rows in FEData and SSFEData.
"id" is the primary key column on both FEData and SSFEData.
Then this will return a list of rows from FEData that are not present in SSFEData:
var notInSSFEData = FEData.AsEnumerable()
.Where(x => SSFEData.Rows.Find((object)x.Field<int>("id")) == null)
.ToList();
If assumption 2 above does not hold (i.e. the "id" field is not the primary key), a slightly more elaborate query is required.
var notInSSFEData = FEData.AsEnumerable()
.Where(x1 => !SSFEData.AsEnumerable().Any(x2 => x2.Field<int>("id") == x1.Field<int>("id")))
.ToList();
1 this requires adding a reference to System.Data.DataSetExtensions (in System.Data.DataSetExtensions.dll).

Get values of tablerow - dataset Windows CE

I have a dataset with a table called "product". this has 3 columns: coding, amount & description.
I want to search a product which I identify by "coding". My Code looks like this:
DataTable table = ds.Tables[0]; // = "Product"
string expression = coding.ToString();
var filtered = table.AsEnumerable()
.Where(r => r.Field<String>("Coding").Equals(expression));
How can I now select for example the matching description for storing it into a variable?
Filtered is not one table row it's a list of table rows. You can iterate throw them and do further operations.
foreach (var row in filtered)
{
Console.WriteLine("{0}, {1}, {2}", row["coding"], row["amount"], row["description"]);
}

c# datatable select last row on a speicfic condition

I have a datatable has data like this format
........ IVR........
.........IVR........
.........IVR........
.........City1......
.........City1......
.........City1......
.........City2......
.........City2......
.........City2......
I want to take the last row of each value. in order words, the rows that are bold now
The challenge is that i wan these three rows in a datatable. I tried to search on internet but i didn't know what is the name of this feature. could you help me please
You can GroupBy() and then select last row with the help of the Last() method.
var result = from b in myDataTable.AsEnumerable()
group b by b.Field<string>("Your_Column_Name") into g
select g.Last();
DataTable filtered = myDataTable.Clone();
foreach(DataRow row in result)
{
filtered.ImportRow(row);
}
Clone clones the structure of the DataTable, including all DataTable schemas and constraints.
This can be implemented in a simple loop using a Dictionary to hold found rows:
var cRows = new Dictionary<string, DataRow>(StringComparer.InvariantCultureIgnoreCase);
foreach (DataRow oRow in oTable.Rows)
{
var sKey = oRow["KeyValue"].ToString();
if (!cRows.ContainsKey(sKey))
{
cRows.Add(sKey, oRow);
}
else
{
cRows[sKey] = oRow;
}
}
This approach will store the last row for each unique value in the column that you nominate.
To move the selected rows into a new DataTable:
var oNewTable = oTable.Clone();
foreach (var oRow in cRows.Values)
{
oNewTable.Rows.Add(oRow);
}
Clone just clones the structure of the current table, not the rows.

Getting duplicates count for each distinct value from a datatable

I've a datatable which has a single text column 'Title' which can have multiple values with duplicates. I can remove the duplicates using a dataview.
DataView v = new DataView(tempTable);
tempTable = v.ToTable(true, "Title");
But how can i get the number of duplicates for each distinct value without any looping?
If you don't want to loop or use Linq, so there is no way to do that but you can use a computed column on the data table with one more condition if applicable with you. That is the data should be in two related tables like this.
DataRelation rel = new DataRelation("CustToOrders", data.Tables["Customers"].Columns["customerid"], data.Tables["Orders"].Columns["customerid"]);
data.Relations.Add(rel);
Given that customerid field as a Foreign key in the Orders table so it has duplicates.
You can get the count of the duplicates this way:
data.Tables["Customers"].Columns.Add("Duplicates",
GetType(Decimal), "Count(child.customerid)");
The way I would get the results that you want would look something like this:
tempTable.Rows.Cast<DataRow>()
.Select(dr => Convert.ToString(dr[0]))
.GroupBy(dr => dr)
.Select(g => new { Title = g.Key, Count = g.Count() });
However, it's actually looping under the hood. In fact, I can't think of a way to do that kind of a grouping without inspecting each record.
The drawback is that the result of that expression is a sequence of anonymous type instances. If you still want the result to be a DataView, you could rewrite the last Select to create a new DataRow with two columns, and shove them into a new DataTable which you pass to the DataView.

Categories