how to subtract two datatables with linq - c#

Is there any way to subtract two datatables in order to have rows of the first datatable that are not in the second one?
I have tried .Except() method like below but it does not work for me.
dt1.AsEnumerable().Except(dt2.AsEnumerable()).CopyToDataTable();
I think i have mistake in using this method but i could not find that?

You can create your own Comparer using IEquailtyComparer
public class CustomDataRowComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
for (int i = 0; i < x.Table.Columns.Count; i++)
{
if (x[i].ToString() != y[i].ToString())
{
return false;
}
}
return true;
}
public int GetHashCode(DataRow obj)
{
return obj.ToString().GetHashCode();
}
}
Later you can call it like:
CustomDataRowComparer myDRComparer = new CustomDataRowComparer();
var result2 = dt1.AsEnumerable().Except(dt2.AsEnumerable(),myDRComparer).CopyToDataTable();

Except will not work because the references are different. Each "copy" of a data row in memory is a different object, thus the equality comparison being made in Except will always return false.
You need to compare with something comparable, like row IDs. A couple ideas how to do this:
var rowsInFirstNotInSecond = dt1.Rows.Where(r1=>!dt2.Rows.Any(r2=>r1.ID == r2.ID));
Or:
var rowsInFirstNotInSecond = dt1.Rows.Where(r1=>dt2.FindById(r1.ID) == null);

I have used the following code to subtract two datatables from each other :
var query =
from row1 in dt1
where !(from row2 in dt2
select row2.ID)
.Contains(row1.ID)
select row1;
this code returns exactly what i want...

This Code works for sure:
var rows =dtFirst.AsEnumerable().Except(dtSecond.AsEnumerable(), DataRowComparer.Default);
DataTable result = null;
if (rows.Count() != 0)
result = rows.CopyToDataTable();

Related

In a C# List how to select all columns + several custom columns, so that in each row all columns are flat (not nested)?

I have a simple List that each row of it has 50 columns. I want return all 50 columns + 3 custom columns but i want make each row of the list like a flat (not nested) object.
Example:
var newList = list.Select(x => new
{ x,
d.CustomColA = x.ColA+10,
d.CustomColB = x.ColB+30,
d.CustomColC = x.ColC+50
});
Result: It works well but each result row is like an nested object:
var row = newList.FirstOrDefault();
row.x.ColA
row.x.ColB
row.x.ColC
.....
row.CustomColA
row.CustomColB
row.CustomColB
Expected Result:
var row = newList.FirstOrDefault();
row.ColA
row.ColB
row.ColC
.....
row.CustomColA
row.CustomColB
row.CustomColB
I used dynamic type and wrote the following code but it did not return expected result:
var newList = list.Select(x =>
{
dynamic d = x;
d.CustomColA = x.ColA+10;
d.CustomColB = x.ColB+30;
d.CustomColC = x.ColC+50;
return d;
//return x;
});
Result in watch panel: 'newList.FirstOrDefault()' threw an exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException'
Update:
Attention: I wrote in my question that i have 50 columns and wrote an
example to show you i do not want name all the columns in the Select!
(I know I can write name of all 53 column in a Select!) So that is not the correct answer.
Attention2: In the real project i have complicated custom columns but i wrote very simple example here to show what i want. Please write your flexible answers. Thank you.
So what you are looking to do is basically map a set of properties. There are libraries for this sort of thing, Automapper is a good one. However, you can also accomplish this yourself with a reflective constructor in an inheriting class. That would look like this:
Assumptions:
Your class with 50 columns is called Cols
You can inherit from Cols
public class CustomColumns : Cols
{
public int CustomColA {
get{
return this.ColA + 10;
}
}
public int CustomColB {
get{
return this.ColB + 30;
}
}
public int CustomColC {
get{
return this.ColC + 50;
}
}
public CustomColumns(Cols cols)
{
string[] localNames = this.GetType().GetMembers().Where(m => m.MemberType == MemberTypes.Property).Select(m => m.Name).ToArray();
string[] ctorNames = cols.GetType().GetMembers().Where(m => m.MemberType == MemberTypes.Property).Select(m => m.Name).ToArray();
string[] names = localNames.Intersect(ctorNames).ToArray();
foreach (string s in names)
{
PropertyInfo propSet = this.GetType().GetProperty(s);
PropertyInfo propGet = typeof(Cols).GetProperty(s);
propSet.SetValue(this, propGet.GetValue(cols, null));
}
}
}
Here is a demo of this working in dotnetfiddle: https://dotnetfiddle.net/AKPYQD
Use an anonymous type within the Select:
var newList = list.Select(x =>
new
{
ColA = x.ColA,
ColB = x.ColB,
ColC = x.ColC,
CustomColA = x.ColA+10;
CustomColB = x.ColB+30;
CustomColC = x.ColC+50;
}).ToList();
The .Select() is creating a new object. Even if you just wrote:
var newList = list.Select(x => new
{ x });
You've get a nested property. You are just going to have to explicitly assign every column to a new property (Do you really need all 50 of them?)

How do I delete a datarow from a datarow array?

I am looping through a array of datarows and when a particular random item is not valid I want to remove that item and get the new total to get another random item.
But when I delete a datarow the datarow does not go away... And yes there is probably a much better way to do this but I am not smart enough to do it..
Instead of removing the row I see this inside
ItemArray = podLps[1].ItemArray threw an exception of type System.Data.RowNotInTableException
//PHASE 1: Get all LPs in the pod and add to collection
List<DataRow> allLps = dtLp.AsEnumerable().ToList();
DataRow[] podLps = allLps.Where(x => x.ItemArray[0].ToString() == loPod).ToArray();
//PHASE 2: Pick a random LP from collection that has valid WAVE1
for (int i = podLps.Count(); i > 0; i--)
{
//Recount items in collection since one may have been removed
int randomIndex = random.Next(podLps.Count());
var randomLpUserId = podLps[randomIndex].ItemArray[1].ToString();
var randomLpWave1 = int.Parse(podLps[randomIndex].ItemArray[2].ToString());
//Get WAVE1 # for selected LP
lpNumberOfLoans = GetNumberOfLoans(session, randomLpUserId);
//check if LP has valid WAVE1 then use this person
if (randomLpWave1 > lpNumberOfLoans)
{
return randomLpUserId;
}
else
{
podLps[randomIndex].Delete();
}
}
look at this example and it should point you in the right direction for removing rows I just tested it and it works
for (int i = myDataTable.Rows.Count - 1; i >= 0; i--)
{
DataRow row = myDataTable.Rows[i]; //Remove
if (myDataTable.Rows[i][0].ToString() == string.Empty)
{
myDataTable.Rows.Remove(row);
}
}
I would suggest to use a List for podLps instead of an array.
Then you can use .RemoveAt as Jaco mentioned (dosn't work for arrays).
DataRow.Delete() just flags the Row to be deleted in the next update of your DataTable.
The easiest method is to convert your array of DataRow[] to a List, call RemoveAt and then convert the list back to an array:
var dest = new List<>(podLps);
dest.RemoveAt(randomIndex);
podLps = dest.ToArray();

One-time sorting with ObjectListView

I have an ObjectListView populated with some data. When the user clicks a button, I do some data analysis and I need to sort rows of my ObjectListView based on results of my analysis - some rows should be on the top of the view, some rows should follow next...
This sorting is one-time sorting - It is not based on any column of the view and next time the analysis results might be completely different.
Firstly, I tried the most basic situation: I just need to put some rows on the top of the view. I checked this similar question:
Custom Sorting with ObjectListView
and I wrote this piece of code:
SortDelegate prevDelegate = myListView.CustomSorter;
IComparer prevComparer = myListView.ListViewItemSorter;
myListView.CustomSorter = delegate(OLVColumn column, SortOrder order)
{
myListView.ListViewItemSorter = new MyComparer(specialItemsList);
};
myListView.Sort();
myListView.ListViewItemSorter = prevComparer;
myListView.CustomSorter = prevDelegate;
where
class MyComparer : IComparer
{
private readonly List<string> specialItemsList;
public SetPrivilegeComparer(List<string> specialItemsList)
{
this.specialItemsList= specialItemsList;
}
public int Compare(object xobj, object yobj)
{
if (!(xobj is MyListItem && yobj is MyListItem))
return 0;
MyListItem x = (MyListItem )xobj;
MyListItem y = (MyListItem )yobj;
if (specialItemsList.Contains(x.strVal) && specialItemsList.Contains(y.strVal))
{
return 0;
}
if (!specialItemsList.Contains(x.strVal) && !specialItemsList.Contains(y.strVal))
{
return 0;
}
if (specialItemsList.Contains(x.strVal) && !specialItemsList.Contains(y.strVal))
{
return 1;
}
if (!specialItemsList.Contains(x.strVal) && specialItemsList.Contains(y.strVal))
{
return -1;
}
return 0;
}
}
The problem is nothing happens when I run the above code, the Compare method is never called. I studied http://objectlistview.sourceforge.net/cs/recipes.html#how-can-i-do-some-fancy-sorting, but I probably did not understand the trick with CustomSorter and ListViewItemSorter - what are responsibilities of these two classes? Thanks for any idea!

Use a numeric value in a linq dynamic query string

I am trying to make a dynamic linq query that will check for values based on a string.
First of all, here's the query:
objQry = from o in m_Db.OBJECTS.Where(whereConditions)
select o;
if(!objQry.Any())
{
return null;
}
The whereConditions variable is a string I build and pass as parameter to find out the values I need. Here's examples of valid string:
OBJ_NAME == \"Sword\" and OBJ_OWNER == \"Stan\"
This will return any item whose name is "Sword" and owner is "Stan;
OBJ_COLOR == \"Blue\" OR OBJ_COLOR == \"Red\"
This will return any item which color is either blue or red.
Up to there, I'm fine, but now I have a problem: I need to check a decimal field. So I've tried this string:
OBJ_NUMBER == 1
But the query returns null even if there are objects which OBJ_NUMBER value is 1. It's a decimal. How can I indicate the query that they need to check for a decimal value?
**** EDIT ****
I have tried to "modify" the value passed so that it looks like this:
"CARD_NUMBER == Convert.ToDecimal(1)"
And now I have a different kind of error telling me this:
LINQ to Entities does not recognize the method 'System.Decimal ToDecimal(Int32)' method, and this method cannot be translated into a store expression.
Any clues anyone? I'm still looking for a way to do this. Thanks!
EDIT 2
You can get an example of how my code is shaped by looking at this question.
Let's come back at this problem. I want to check decimal values. Let's say that OBJ_NUMBER is a decimal field.
Using Dynamic Linq, I tried to read the decimal field. Say that I want to get each object which number is 1.27. The whereConditions field would then be shaped like this:
OBJ_NUMBER == 1.27
But then I would get an Invalid real literal '1.27' error. I don't know why.
So I have tried Gert Arnold's solution and done this instead:
decimal bDecimal = decimal.Parce(valueToParse);
param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = bDecimal };
valuesToUse.Add("CARD_NUMBER == #cardNumber");
listParams.Add(param);
But I ended up having 2 problems:
The first problem is that my whereConditions string is shaped this way:
CARD_NUMBER == #cardNumber
But I get the following error:
No property or field 'cardNumber' exists in type 'CARD'
Leading me to believe that it cannot make the link between the object parameter and the string used to do the query.
As you can see, I have a list of Params. This is because I cannot know for sure how many parameters the user will chose. So each time the user enters a new search field, I have to create a new ObjectParameter and store it in a list. Here's how I try to do the thing after:
ObjectParameter[] arrayParameters = listParams.ToArray();
// Convert the list to an array
And then, when I try to make the query:
cardQry = from c in mDb.CARD.Where(whereConditions, arrayParameters)
select c;
But to no avail.
RESULTS
Based on the answered question below, I have developped something "awful", yet functional.
First of all, I ignore every decimal fields because I could never reach them with dynamic linq. Instead, I do this:
var valuesToParse = keyValuePair.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
// Here I parse the value and, if that's the case, the symbol.
decimal baseValue = decimal.Parse(valuesToParse[0]);
if (valuesToParse.Count() > 1)
{
string baseMethod = valuesToParse[1];
if (baseMethod == ">" || baseMethod == ">=")
{
if (baseMethod == ">=")
{
baseValue--;
}
// The list is actually like this: Dictionary<string, object> list = new Dictionary<string, object>();
list.Add("low", baseValue);
// I kind of activate a tag telling me that the user is looking for a higher value.
cardHigher = true;
}
else
{
if (baseMethod == "<=")
{
baseValue++;
}
list.Add("low", baseValue);
cardLower = true;
}
}
else
{
//lowParam = new ObjectParameter("dec", typeof(decimal)) { Value = baseValue };
list.Add("low", baseValue);
}
cardNumberActivated = true;
At the end, when I get the list of objects, I do this:
if (list.Count > 0)
{
(example)
if (cardNumberActivated)
{
if (cardHigher)
{
q = mDb.CARD.Where("CARD_NUMBER >= #0", list["low"]).ToList();
}
else if (cardLower)
{
q = mDb.CARD.Where("CARD_NUMBER <= #0", list["low"]).ToList();
}
else
{
q = mDb.CARD.Where("CARD_NUMBER == #0", list["low"]).ToList();
}
}
}
// Here we get the orinalData with the basic filters.
listToReturn.AddRange(cardQry);
if (q != null)
{
//listToReturn.AddRange(q);
for (int i = 0; i < listToReturn.Count; i++)
{
var priceList1 = listToReturn[i];
if (!q.Any(_item => _item.CARD_NUMBER == priceList1.CARD_NUMBER))
{
listToReturn.RemoveAt(i);
i--;
}
}
}
And it works. This is not an elegant way to make it work, but I can validate the fields the way I wanted, and for this, I am thankful at last.
You should not build a query string with inline predicate values. Use parameters in stead. Then will also be able to specify the type:
var whereConditions= "it.CARD_NUMBER = #cardNumber";
var param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = 1 };
objQry = from o in m_Db.OBJECTS.Where(whereConditions, param);
Edit
I don't know what doesn't work in your code. Here's just a random piece of working code derived from one of my own projects:
var param1 = new ObjectParameter("dec", typeof(decimal)) { Value = 90000m };
var param2 = new ObjectParameter("int", typeof(int)) { Value = 90000 };
var q = ValueHolders.Where("it.DecimalValue >= #dec OR it.IntegerValue > #int",
param1, param2).ToList();
Note that param1, param2 could also be an array of ObjectParameter.

How to get what exist in the first datatable and not exist in the second data table in a third one?

Q:
I have two queries each one return a DataTable. I wanna to return another DaTaTable as a result of(What exist in the first DataTable AND Not Exist(NOT IN) the second DataTable).
My queries:
EDIT : I make it general:
1-DT1:
DataTable dt1 = cc1assiscrsevalDAL.GetAll(int.Parse(Session["course_prof"].ToString()), 0);
2-DT2:
DataTable dt2 = cc1assiscrsevalDAL.GetConfirmedEval(int.Parse(Session["course_prof"].ToString()));
Note:batch_no,crsnum,lect_code are the composite primary key
What is the best way to do that?(wise performance).
I wanna also to do that with LINQ.(if possible).
var dt = dt1.AsEnumerable().Except(dt2.AsEnumerable(), new CustomDataRowEqualityComparer()).CopyToDataTable();
public class CustomDataRowEqualityComparer: IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
return ((int)x["crsnum"]) == ((int)y["crsnum"])
&& ((int)x["crsnum_e"]) == ((int)y["crsnum_e"])
&& ((int)x["crstteng"]) == ((int)y["crstteng"]);
}
public int GetHashCode(DataRow obj)
{
return ((int)obj["crsnum"]) ^ ((int)obj["crsnum_e"]) ^ ((int)obj["crstteng"]) ;
}
}
There is an extension method in the linq called Except which solves your problem but we need to create a separate class for that which i have done in the above code.
select *
from (*target_query*) t
join
(
select batch_no,crsnum,lect_code from (*target_query*) q
except
select batch_no,crsnum,lect_code from cc1assiscrseval
) temp on temp.batch_no = t.batch_no and temp.lect_code = t.lect_code, temp.crsnum = t.crsnum
Pretty dirty solution, but I think you could simplify it by getting only desired batch_no, crsnum and lect_code, without performing first query twice. But you'll have to figure it out yourself.
Linq provides you with an Except method so you could do something like this
var _differences = dt1.AsEnumerable.Except(dt2.AsEnumerable()); // No checked or tested in VS
You could also first get the relevant columns by using something like this:
var x = From a In dt1
Select (...relevant columns)
var y = From a In dt2
Select (...relevant columns)
And then do the above except.
HTH!

Categories

Resources