reorder datatable rows with a specific sequence of values - c#

I have a datatable that looks like this:
IDDOC DOCNAME
1 mydoc1
153 mydoc2
98 mydoc3
1327 mydoc4
241 mydoc5
I would like to find a way to reorder the rows in that datatable by basing on a specific sequence of ID
For example, with a sequence like this : 1327,98,1 the expected output would be :
IDDOC DOCNAME
1327 mydoc4
98 mydoc3
1 mydoc1
153 mydoc2 (not in my sequence so this row comes at the end)
241 mydoc5 (not in my sequence so this row comes at the end)
I was thinking about creating a new empty database and adding the row with the IDDOC coming first, then second, then third in the sequence, then finally all the rows not present in my sequence but i was wondering if something cleaner already existed.
Thank you a lot for your help!

You could use this approach:
dt = dt.AsEnumerable()
.OrderBy(r => r.Field<int>("IDDOC") == 1327 ? 0 : 1)
.ThenBy(r => r.Field<int>("IDDOC") == 98 ? 0 : 1)
.ThenBy(r => r.Field<int>("IDDOC") == 1 ? 0 : 1)
.CopyToDataTable();
If you want a dynamic list of id's and the DataTable should be ordered by this sequence:
List<int> docIdSequence = new List<int>{ 1327, 98, 1 };
dt = dt.AsEnumerable()
.OrderBy(r => {
int ix = docIdSequence.IndexOf(r.Field<int>("IDDOC"));
return ix >= 0 ? ix : int.MaxValue;
})
.CopyToDataTable();
Note that CopyToDataTable throws an exception if there are no input DataRows. So you have to check if dt.Rows.Count > 0 before you use above code.

Related

c# calculate distinct value in datatable

what i have in my datatable
Resource
1 // 1 represent normal
1
2 // 2 represent sql
2
3 // 3 css
4 // 4 unicode
4
4
how can i perform calculation so that i could display the value in a textbox
normal 2
sql 2
css 1
unicode 3
total hits 9
what ive tried so far
var result = my_datatable.AsEnumerable().Sum(x => Convert.ToInt32(x["Resource"]));
string result2 = result.ToString();
totalTxtBox.Text = result2;
but it calculate the whole column (output is: 24 instead of 9)
Use the following example-
int[] res = { 1,1,2,2,3,4,4,4};
var words = res.AsEnumerable().GroupBy(x => x);
foreach (var x in words)
{
Console.WriteLine(x.Key+"-"+x.Count());
}
It will print output as-
1-2
2-2
3-1
4-3
You can try to use Distinct()
var result = my_datatable.AsEnumerable().Distinct().Sum(x => Convert.ToInt32(x["Resource"]));
Distinct() Returns distinct elements from a sequence by using the default equality comparer to compare values.
for more info: https://msdn.microsoft.com/en-us/library/bb348436(v=vs.110).aspx
You can try to use this example as your reference. Firstly, I get the Distinct values from the datatable and converted it to list and then use the sum function.
Updated Answer:
DataTable my_datatable = new DataTable();
my_datatable.Columns.Add("Value", typeof(int));
my_datatable.Columns.Add("Type", typeof(string));
my_datatable.Rows.Add(1, "Normal");
my_datatable.Rows.Add(1, "Normal");
my_datatable.Rows.Add(2, "SQL");
my_datatable.Rows.Add(2, "SQL");
my_datatable.Rows.Add(3, "CSS");
my_datatable.Rows.Add(4, "UNICODE");
my_datatable.Rows.Add(4, "UNICODE");
my_datatable.Rows.Add(4, "UNICODE");
var distinctIds = my_datatable.AsEnumerable()
.Select(s => new {
value = s.Field<int>("value"),
})
.Distinct().ToList();
int total = distinctIds.Sum(item => item.value);
I figured it out by myself
use this line (linq) in order to filter out which value you want
int normalcount = my_datatable
.AsEnumerable()
.Where(r => r.Field<string>("Resource") == "1")
.Count();
try to change this line to filter out which value, according to your column value
.Where(r => r.Field<string>("Resource") == "2")
.Count();

how to avoid two columns contain 0 in linq

assume as i have to shown the list of value in the screen
iam taking the list using linq
Student English Hindi Tamil MArathi
------- --------- ------- ------- ---------
Deepan 56 65 34 45
Mohan 45 34 0 23
Murali 56 89 0 0
Assume that i have these values in db.....
I dont want to show if (tamil and marathi ) is 0.....if both r contains 0 means ....
i have to avoid that row while taking from database using linq ...eg(murali)...but i dont want to avoid Mohan.....pls give me the linq query
Now i tried this
var ulist = (from c in CustomerTransactions
where c.TransTypeID==12
select new
{
Student=c.Student,
English=c.English,
Hindi=c.Hindi,
Tamil=c.Tamil,
Marathi=c.Marathi
}).ToList().OrderBy(b => b.Student).Where(x => x.Marathi!=0 );
You can combine several conditons in a single where-statement:
.Where(x => x.Marathi !=0 && x.Tamil != 0)
This will select only those entries where both conditions pass (meaning both Marathi and Tamil are not 0).
This is what you can use if Lambda is fine for you. You can combine all the 3 checks in first Where clause itself, instead of specifying the conditions at 2 different places.
CustomerTransactions.Where(c => c.TransTypeID==12 && !(c.Marathi == 0 && c.Tamil == 0))
.OrderBy(c => c.Student)
.Select(c => new {
Student=c.Student,
English=c.English,
Hindi=c.Hindi,
Tamil=c.Tamil,
Marathi=c.Marathi
}).ToList();

Sort column in DataTable with same values

I have a question about sorting in Datatable. I have a table like below and want to sort it from small to big. The problem is when i have same numbers, i want to have the first as the last and so on...
Table:-----------------------------------After Sorting:
Name Bit Size Name Bit Size (corrected)
A 0 1 A 0 1
C 1 2 C 1 2
B 1 3 B 1 3
D 1 1 D 1 1
Result that i want:
Name Bit Size (corrected)
A 0 1
D 1 1
B 1 3
C 1 2
My Code:
arraySBit.DefaultView.Sort = "Bit";
arraySBit = arraySBit.DefaultView.ToTable();
You can use Linq-To-DataTable:
var tblSorted = table.AsEnumerable()
.OrderBy(r => r.Field<int>("Bit"))
.CopyToDataTable();
Edit: But actually DataView.Sort should also work (tested).
Since you have edited your question. Your requirement seems weired. If the Bit is the same you don't want to order by something but you want to reverse the "order" of the rows of the equal rows (so the Ordinal position in the DataTable).
This does what you want although i'm not sure that it's really what you need:
DataTable tblSorted = table.AsEnumerable()
.Select((Row, Ordinal) => new {Row,Ordinal})
.OrderBy(x => x.Row.Field<int>("Bit"))
.ThenByDescending(x => x.Ordinal)
.Select(x => x.Row)
.CopyToDataTable();
Basically it passes the index of the row in the table via this overload Enumerable.Select into an anonymous type. Then it'll sort by Bit first and the index/ordinal second.
A workaround,
After these lines, you should have result in arrySBit as you want
DataTable arrySBitClone = arrySBit.Copy();
arrySBit.DefaultViewSort.Sort = "Bit";
bool different = false;
for(int i=0; i<arrySBit.Rows.Count; i++)
{
if(arrySBit.Rows[i]["Bit"]!=arrySBitClone.Rows[i]["Bit"])
{
difference = true;
break;
}
}
if(!different)
{
arrySBit = arrySBit.Copy();
}
if i'm getting right your question.
why not use Select?
DataTable dt = arraySBit.Select("", "Bit, Size").CopyToDataTable();
The first parameter of the Select Method is a condition to filter, the second is an Order By, so it should work

Compare and delete datatable row using C#:

I have two tables such as DTTable1 and DTTable2. It has the following records.
DTTable1:
ItemID Specification Amount
--------- --------------- ---------
1 A 10
1 B 20
1 C 30
DTTable1:
ItemID Specification Amount
--------- --------------- ---------
1 A 10
1 B 20
1 C 30
2 A 10
2 B 20
3 A 10
3 B 20
Here I want to compare these two tables. If DTTable1 records present in DTTable2(consider only ItemID) then remove the corresponding rows which has the ItemID same as DTTable1.
I have tried foreach and forloop.
Using ForEach:
foreach (DataRow DR in DTTable2.Rows)
{
if (DR["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
{
DTTable2.Rows.Remove(DR);
}
}
DTTable2.AcceptChanges();
It showed the error,
"Collection was modified; enumeration operation might not execute". So I used For Loop, It also not given the desired result.
Using For Loop:
for (int i = 0; i < DTTable2.Rows.Count; i++)
{
if (DTTable2.Rows[i]["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
{
DTTable2.Rows.RemoveAt(i);
}
}
DTTable2.AcceptChanges();
But sometimes, the second row doesn't remove from the table. I get the final DataTable as
ItemID Specification Amount
--------- --------------- ---------
1 B 20
2 A 10
2 B 20
3 A 10
3 B 20
How to solve this? What is the simplest method to do this?
You can't modify a collection while you are enumerating it as in your first example. You should instead get a list of the rows to delete and then delete them.
List<DataRow> rowsToDelete = new List<DataRow>();
foreach (DataRow DR in DTTable2.Rows)
{
if (DR["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
rowsToDelete.Add(DR);
}
foreach (var r in rowsToDelete)
DTTable2.Rows.Remove(r);
Or, inline using linq:
DTTable2.Rows
.Where(DR => DR["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
.ToList()
.ForEach(r => DTTable2.Rows.Remove(r));
Your second example fails because once a row is removed, the indexes of subsequent rows change but you continue to increment i, which effectively skips over the row immediately following the deleted row. There are two ways around this:
for (int i = DTTable2.Rows.Count - 1; i >= 0; i--)
if (DTTable2.Rows[i]["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
DTTable2.Rows.RemoveAt(i);
Or
int i = 0;
while (i < DTTable2.Rows.Count)
{
if (DTTable2.Rows[i]["ItemID"].ToString() == DTTable1.Rows[0]["ItemID"].ToString())
DTTable2.Rows.RemoveAt(i);
else
i++;
}
Side note: I wonder if you really mean to always compare to row 0's data, given your description. Perhaps you meant to compare all rows like the following instead (although not optimal at all)?
if (DTTable1.Any(r => DTTable2.Rows[i]["ItemID"].ToString() == r["ItemID"].ToString()))
Try to use:
var list = DTTable2.Rows.ToList();//create new list of rows
foreach (DataRow DR in list)
{
//if bla bla bla
DTTable2.Rows.Remove(DR);
}
You can use LINQ to DataTable:
var result = DTTable1.AsEnumerable()
.Where(row => !DTTable2.AsEnumerable()
.Select(r => r.Field<int>("ItemID"))
.Any(x => x == row.Field<int>("ItemID"))
).CopyToDataTable();
Well, guys. I had an almost identical situation and no matter how I tried to do it, I kept running into problems. My solution was not to try and loop through the DataTable at all. I made an array from the first DataTable (this contained the rows that needed to be deleted from the second DataTable). Loop through the Array and use a select to remove the row that matched in the DataTable. Since it wasn't a loop, and it could match any row in the DataTable, I quit getting errors.
Note: Original DataTable is a 1-column table that contains what you are trying to remove from the second DataTable by row (doesn't matter how many columns it has). Replace "LinkName=' w/ the column name for your 1-column table.
public DataTable RemoveDuplicateRows(DataTable OriginalLinks, DataTable UpdatedLinks) // OriginalLinks is a 1-col dt to delete from new dt
{
ArrayList arrOriginalLinks = new ArrayList();
if (OriginalLinks.Rows.Count > 0)
{
foreach (DataRow dr in OriginalLinks.Rows)
{
arrOriginalLinks.Add(dr["LinkName"]);
}
}
if (OriginalLinks.Rows.Count == 0)
{
// do nothing
}
else
{
for (int i = OriginalLinks.Rows.Count - 1; i >= 0; i--)
{
string filter = "LinkName='" + arrOriginalLinks[i].ToString() + "'";
UpdatedLinks.Select(filter).ToList().ForEach(r => r.Delete());
}
}
return UpdatedLinks;
}
This returns the second DataTable with just the rows left that didn't match a value found in the first DataTable.

How to optimise this LINQ Query

I have this query
Dasha.Where(x => x[15] == 9).ForEachWithIndex((x,i) => dd[Sex[i]][(int)x[16]]++);
This query is finding that element in Dasha whose 15th index value is 9 and if yes it increments dd[Dashaindex][x[16]] value.
Here Dasha is double[100][50] and dd is double[2][10] and Sex is byte[ ] and can only have value 0 or 1. 0 for Male and 1 for Female
x[15] can only be between 0 to 9 (both inclusive). Same rule for x[16].
It is giving me right results.
I tried optimising this to
Dasha.ForEachWithIndex((x,i) =>
{
if(x[15] == 9)
dd[Sex[i]][(int)x[16]]++
});
This is giving me wrong results. Where am i doing wrong?
My ForEachWithIndex is like
static void ForEachWithIndex<T>(this IEnumerable<T> enu, Action<T, int> action)
{
int i = 0;
foreach(T item in enu)
action(item, i++);
}
This is just a partial answer (too long for a comment) in regards to
Dasha.ForEachWithIndex((x,i) => {
if(x[15] == 9)
dd[Sex[i]][(int)x[16]]++ });
This is giving me wrong results. Where am i doing wrong?
In the first case you filter the Dasha list of 100 items down to n items, then you iterate over these n items.
in the second case you iterate over all 100 items. So the index will be different, and the value you get from Sex[i] for each row will be different
e.g.
Dasha[0] != Dasha.Where(x => x[15] == 9)[0]
unless Dasha[0][15] == 9
You need to save original indexes before Where:
Dasha.Select((x,i) => new {x = x, i = i})
.Where(a => a.x[15] == 9)
.ForEach(a => dd[Sex[a.i]][(int)a.x[16]]++);
Following will give you same result as of first query.
int counter=0;
Dasha.ForEachWithIndex((x,i) =>
{
if(x[15] == 9)
{
dd[Sex[counter]][(int)x[16]]++;
counter++;
}
})

Categories