I have multiple rows in a datatable, see a sample below:
Existing Table
Name Date Value Type
ABC(I) 11/11/2013 12.36 I
DEF(I) 11/11/2013 1 I
GHI(I) -do- -do- I
JKL(P) P
MNO(P) P
PQR(D) D
STU(D) -d0- -do- D
Required Table
Name Date Value Type
JKL(P) P
MNO(P) P
PQR(D) D
STU(D) -d0- -do- D
ABC(I) 11/11/2013 12.36 I
DEF(I) 11/11/2013 1 I
GHI(I) -do- -do- I
COndition to use
Sorting should be as per the column Type. Now I need a small change in order of the rows to be shown in the gridview. That is rows of Payment will come first then all Dues and at last all Interests types will come.
What I tried:
Sorting of column but it was not what I need.
Custom Grouping suggested by Tim Schmelter here
Code was:
public DataTable GroupBy(string i_sGroupByColumn, string i_sAggregateColumn, DataTable i_dSourceTable)
{
DataView dv = new DataView(i_dSourceTable);
//getting distinct values for group column
DataTable dtGroup = dv.ToTable(true, new string[] { i_sGroupByColumn });
//adding column for the row count
dtGroup.Columns.Add("Count", typeof(int));
//looping thru distinct values for the group, counting
foreach (DataRow dr in dtGroup.Rows) {
dr["Count"] = i_dSourceTable.Compute("Count(" + i_sAggregateColumn + ")", i_sGroupByColumn + " = '" + dr[i_sGroupByColumn] + "'");
}
//returning grouped/counted result
return dtGroup;
}
I dont know where and what I am lacking/missing. Kindly help.
try linq to order your table:
var query = dtGroup.AsEnumerable()
.OrderBy(c=> c.Field<DateTime?>("Date"))
.ThenByDescending(c=> c.Field<string>("Name"));
DataView dv2 = query.AsDataView();
If I understand correctly you want first sorting on P, D, I and then on date
Dictionary<string, int> sortDictionary = new Dictionary<string, int>();
sortDictionary.Add("P", 1);
sortDictionary.Add("D", 2);
sortDictionary.Add("I", 3);
var q = from row in dtGroup.AsEnumerable()
let type = sortDictionary[row.Field<string>("Name").Substring(4, 1)]
orderby type, row.Field<string>("Name")
select row;
foreach (var r in q)
{
string x = r["Name"].ToString() + r["Date"].ToString();
}
Related
I am trying to concatenate all the columns and then all the rows of a DataTable.
I have tried below code:
var student = new DataTable();
student.Columns.Add("Name", typeof(string));
student.Columns.Add("Country", typeof(string));
for (int i = 0; i <= 3; i++)
{
DataRow dr = student.NewRow();
dr["Name"] = "Student" + i;
dr["Country"] = "India";
student.Rows.Add(dr);
}
List<DataRow> rows = (from DataRow row in student.Rows select row).ToList();
var paramValues = rows.Select(x => string.Format("({0},{1}),", x.ItemArray[0], x.ItemArray[1])).Aggregate((x, y) => x + y).TrimEnd(',');
Console.WriteLine(paramValues);
This is giving me output like (Student0,India),(Student1,India),(Student2,India),(Student3,India)
This code is fixed for two columns, how can I make it general for any number of columns?
It can be something like this
var paramValues = String.Join(",",
rows.Select(x => "(" + String.Join(",", x.ItemArray) + ")" ));
Perhaps you could consider a more traditional approach instead. Sometimes I find Linq less readable and not every scenario are a good fit to use it.
In your case (if even possible) I think that a normal loop conveys better your intentions.
StringBuilder sb = new StringBuilder();
foreach (DataRow row in student.Rows)
{
// Concatenate all
sb.Append("(" + string.Join(",", row.ItemArray) + ")");
sb.AppendLine(); // Add a new line (or a sb.Append(",") for a comma)
}
Console.WriteLine(sb.ToString());
NOTE
The code above assumes a lot about your table contents. For example, null values could wreak havoc here.
I have a combobox which contains different values:
public static DataTable GetStates()
{
DataTable myStates = new DataTable();
myStates.Columns.Add("Name", typeof(string));
myStates.Columns.Add("Location", typeof(string));
myStates.Rows.Add("1", "USA");
myStates.Rows.Add("2", "USA");
myStates.Rows.Add("3", "Canada");
return myStates;
}
I want this to be in BindStates function which will count the number of occurences of "USA" and so on.
public void BindStates(DataTable states)
{
int numberUsa = 0;
foreach (DataRow row in states.Rows)
{
if (row[1].ToString() == "USA")
{
numberUsa++;
}
}
Console.WriteLine(numberUsa.ToString());
}
You can do this by using LINQ against the datatable. So what I'm doing is creating an IGrouping that is grouped by the second object in the DataRow ItemArray. The second step is to just count how many items are in the "USA" Group.
// Get datatable with data
var states = GetStates();
// Create grouping of rows
var grouping = states.AsEnumerable().GroupBy(row => row.ItemArray[1]).ToList();
// Count how many rows are in the "USA" group
var numOfUSA = grouping.First(group => group.Key == "USA").Count();
Obviously this code can be improved with null checking etc, this is just to get you started. Good Luck!
I have DataTable object, which holds some "tree data structure". Data is not stored in any database, I just use DataTable to manipulate data without SQL server.
My data looks like this (indents are only for better reading here):
DataTable dtCategories = GetCategoriesAsDataTable();
id name parentId
int string int
----------------------
1 One 0
2 OneA 1
3 OneB 1
4 Two 0
5 TwoA 4
6 TwoB 4
7 TwoAA 5
8 TwoAB 5
So far - I was thinking about selecting first level with "where parentId = 0" and putting this to separate DataTable, like this:
DataTable dtFirstLevel = dtCategories.Select("[parentId] = 0");
// and after this - create DataTable for second level
// but I don't know how can I use "IN" clause here
DataTable dtSecondLevel = dtCategories.Select(?????????);
How can I select only first 2 levels of tree?
How can I select this without SQL server (by using only data objects)?
Maybe this helps:
var rows = table.AsEnumerable();
var parents = rows.Where(r => !r.Field<int?>("parentId").HasValue);
var children = rows.Where(r => r.Field<int?>("parentId").HasValue);
var secondLevel = from parent in parents
join child in children
on parent.Field<int>("id") equals child.Field<int?>("parentId").Value
select child;
var both = parents.Concat(secondLevel).CopyToDataTable();
Note that i've used Nullable<int> instead of 0 for a parent since that is more readable and less prone of errors. Here is your sample data:
var table = new DataTable();
table.Columns.Add("id", typeof(int));
table.Columns.Add("name", typeof(string));
table.Columns.Add("parentId", typeof(int));
table.Rows.Add(1, "One", (int?)null);
table.Rows.Add(2, "OneA", 1);
table.Rows.Add(3, "OneB", 1);
table.Rows.Add(4, "Two", (int?)null);
table.Rows.Add(5, "TwoA", 4);
table.Rows.Add(6, "TwoB", 4);
table.Rows.Add(7, "TwoAA", 5);
table.Rows.Add(8, "TwoAB", 5);
Result:
1 One
4 Two
2 OneA 1
3 OneB 1
5 TwoA 4
6 TwoB 4
Since you want to stay with 0 instead of int?:
var parents = rows.Where(r => r.Field<int>("parentId") == 0);
var children = rows.Where(r => r.Field<int>("parentId") != 0);
var secondLevel = from parent in parents
join child in children
on parent.Field<int>("id") equals child.Field<int>("parentId")
select child;
I think this function might help you figure out the level of tree of each entry so you can use it in your selection:
public int level(DataTable dt, DataRow row)
{
int parentid = int.Parse(row[2].ToString());
if (parentid == 0)
return 1;
else
return 1 + level(dt, GetDataRow(dt,parentid ));
}
public DataRow GetDataRow(DataTable dt, int id)
{
foreach (DataRow r in dt.Rows)
{
if (int.Parse(r[0].ToString()) == id) return r;
}
return null;
}
You have a couple of options to your problem. As proposed by #Ali, you could use recursion like this:
public int level(DataTable dt, DataRow row)
{
int parentid = int.Parse(row[2].ToString());
if (parentid == 0)
return 1;
else
return 1 + level(dt, GetDataRow(dt,parentid ));
}
public DataRow GetDataRow(DataTable dt, int id)
{
foreach (DataRow r in dt.Rows)
{
if (int.Parse(r[0].ToString()) == id) return r;
}
return null;
}
But the problem is that you'll end up iterating though every element and then using recursion on every iteration. If you have absolutely no data relationship between your columns and their level in the tree, besides a parentId, then this is your only solution.
On the other hand, if you do have a relationship, where you have name[level of tree] like Name[A] is tree level 1 and Name[AB] is tree level two with the right node, then iteration through each like:
foreach (DataRow r in dt.Rows)
{
//Pull out the element
//Check the element's level
//Add it to the result set if level <= 2
}
I'd personally prefer to solve the problem by actually building a tree structure or using a SQL WHERE clause, but it's hard to justify the time on it. Depending on where you get this data from, you may also be able to add an additional column which tells you which level the node is in depending on where it's inserted. If it has a grandparent (i.e. two parent nodes) you don't include it in the result set.
DataTable level1 = (from t in dtCategories.AsEnumerable()
where t.Field<int>("parentId") == 0
select t).CopyToDataTable();
DataTable level2 =(from t1 in dtCategories.AsEnumerable()
join t2 in dtCategories.AsEnumerable()
on t1.Field<int>("id") equals t2.Field<int>("parentId")
where t1.Field<int>("parentId") == 0
select t2).CopyToDataTable();
Another way to do it, this will give you a new object which contains the level and the row item itself. This will work for n number of levels...
var nodes = table.AsEnumerable();
//var nodes = new List<TreeNode>();
var parentId = 0;
var countLevel = 0;
var allNods = new List<dynamic>();
while (nodes.Any(p => p.Field<int>("parentId") == parentId))// && countLevel < 2)
// countlevel< 2 only to give you the first 2 levels only...
{
var nodesWithLevel = nodes.Where(p => p.Field<int>("parentId") == parentId)
.Select(p => new { Level = parentId, Node = p });
allNods = allNods.Concat<dynamic>(nodesWithLevel).ToList();
parentId++;
countLevel++;
}
The code currently expects that the root nodes have parentId = 0. Could be changed to null, too of cause...
I have a Datatable that contains a column called Tags, Tags can have values such as
row[0] = Tag1
row[1] = Tag1, Tag2
row[2] = Tag2, Tag3
row[3] = Tag1, Tag2, Tag3
row[4] = Tag4, Tag6
and are seperated by comma's
etc..
I have the value of Tags for the current document and have run a query to select all other documents that have either of the Tags in there row. for example lets say the current document Tags are (Tag1, Tag2, Tag3)
so from the example rows above all the rows above are returned apart from row[4]
Here is the bit i'm lost with, i now want to sort the datatable by how many tags are matched with the current document. so for the example i've talked about so far they should be ordered
row[3] = Tag1, Tag2, Tag3
row[1] = Tag1, Tag2
row[2] = Tag2, Tag3
row[0] = Tag1
Not used linq before but was told it could do this.
so far i have
var query = from c in dt.AsEnumerable()
orderby c.Field<string>("Tags").CompareTo(dr["Tags"]) ascending
select c;
Use the intersect method. here is a sample
//test data
DataTable table = new DataTable();
var column = table.Columns.Add("Tags", typeof(string));
table.Rows.Add("Tag1");
table.Rows.Add("Tag1,Tag2");
table.Rows.Add("Tag2,Tag3");
table.Rows.Add("Tag1,Tag2,Tag3");
table.Rows.Add("Tag4,Tag6");
string[] currentTags = new string[] { "Tag1", "Tag2", "Tag3" };
//actual code
var a = from row in table.AsEnumerable()
let cData = (row["Tags"] as string).Split(new char[] { ',' }).Intersect(currentTags)
orderby cData.Count() descending
select cData;
I think something like this will fit the bill. Try it out:
// Here, I construct a simple table for demonstration
var table = new DataTable();
var column = table.Columns.Add("Tags", typeof(string));
table.Rows.Add("Tag1");
table.Rows.Add("Tag1, Tag2");
table.Rows.Add("Tag2, Tag3");
table.Rows.Add("Tag1, Tag2, Tag3");
table.Rows.Add("Tag4, Tag6");
// The separator is convenient for using the string.Split override
// that strips empty results
var separator = new[] { ",", " " };
// For the demo, we'll sort by number of tags matching the third row
var current = table.Rows[2];
// This splits the string into an array for convenient processing later
var currenttags = current.Field<string>("Tags")
.Split(separator, StringSplitOptions.RemoveEmptyEntries);
// The query splits out each tags field into an array convenient for processing,
// counts the number of tags contained in the currenttags array,
// sorts, and then selects the entire row.
var query = from row in table.AsEnumerable()
let tags = row.Field<string>("Tags")
.Split(separator, StringSplitOptions.RemoveEmptyEntries)
let count = tags.Count(t => currenttags.Contains(t))
orderby count descending
select row;
string[] Tags = dr["Tags"].ToString().Split(new char[] { ',' });
string SqlClause = "";
for (int i = 0; i < Tags.Length; i++)
{
if (i != Tags.Length - 1)
{
SqlClause += "Tags LIKE '%" + Tags[i] + "%' OR ";
}
else
{
SqlClause += "Tags LIKE '%" + Tags[i] + "%'";
}
}
DataTable dt = ArticleCollection(SqlClause);
var seperator = new[] { ",", " " };
var current = dr["Tags"].ToString();
var currenttags = dr.Field<string>("Tags").Split(seperator, StringSplitOptions.RemoveEmptyEntries);
DataTable query = (from row in dt.AsEnumerable()
let tags = row.Field<string>("Tags").Split(seperator, StringSplitOptions.RemoveEmptyEntries)
let count = tags.Count(t => currenttags.Contains(t))
orderby count descending
select row).CopyToDataTable();
for (int i = 0; i < query.Rows.Count; i++)
{
if (query.Rows[i]["Title"].ToString() == dr["Title"].ToString())
{
query.Rows[i].Delete();
}
}
TagsRepeater.DataSource = query;
TagsRepeater.DataBind();
}
}
DataTable ArticleCollection(string whereClause)
{
DataSet ds = TreeHelper.SelectNodes("/%", false, "CriticalCare.Conclusion;CriticalCare.Literature;CriticalCare.Theory", whereClause, " ", -1, true);
DataTable dt = new DataTable();
if (!DataHelper.DataSourceIsEmpty(ds))
{
for (int i = 0; i < ds.Tables.Count; i++)
{
dt.Merge(ds.Tables[i]);
}
return dt;
}
return null;
}
}
also what i want to do is if more than one article matches the same amount of tags, is order them by there order in relation to the current document in the site tree. which bit of linq would i do that with? is it easy to do that in the same query? is there away of adding a count column to the datatable?
I have a table like this in my Linq to sql class :
ID CL1 CL2 CL3 ... CL20
-- ---- ---- ----- ------
1 12 35 54 .... 44
2 11 35 78 ..... 75
data is not important in this example.
I need to access to each column with their index.
for example to reach data in CL3 like this:
var x = db.myTable.single(a=>a.ID==1)[3];
can anyone help me please?
You could convert your result to a DataTable like this
public static DataTable ConvertToDataTable<T>(IList<T> list)
{
var dt = new DataTable();
var properties = typeof(T).GetProperties();
foreach (var pi in properties)
dt.Columns.Add(pi.Name, pi.PropertyType);
foreach (T element in list) {
var row = dt.NewRow();
foreach (var pi in properties)
row[pi.Name] = pi.GetValue(element, null);
dt.Rows.Add(row);
}
return dt;
}
and then you can access the columns by name or by index.
var dt = ConvertToDataTable<test>(list);
var CL5 = dt.Rows[0][5];
var CL5_by_name = dt.Rows[1]["CL5"];
Properties in the object are not necessarily in the same order the columns are in the database.
You could do reflection to select a property by index but that wouldn't make sense. You should use names of the columns instead.
Based on your comment that the columns have name ending with a digit here is what you can do.
int columnIndex = 3;
var property = (from p in db.myTable.GetType().GetProperties()
where p.Name.EndsWith(columnIndex.ToString())
select p).First();
var record = db.myTable.single(a=>a.ID==1);
var x = property.GetValue(record, null)