i have a table with a column named size which will have values big,medium and small. wat i want is i want to sort this column in such a way that all rows with size big should come first, then rows with size medium and finally rows with size small.
is there a way by which i can achieve this?
edit: it is a data table which i add to a dataset.
edit: wat if the initials of the words, in this case b,m and s, are not in alphabetical order. in that wat am i supposed to do coz i have another application as well where i'll be required to sort a column having value High, normal and low.
Example
DataTable dt = ds.Tables[0];
DataView dv = new DataView(dt);
dv.Sort = sortExpression + direction; //sortexpression will be fieldname direction will be ascending descending
GridView1.DataSource = dv;
GridView1.DataBind()
Have a look at this article: Express Yourself with Expression-based Columns
http://msdn.microsoft.com/en-us/library/ms810291.aspx
OF COURSE IT WORKS!
DataSet myDs = new DataSet();
DataTable myDt = new DataTable("Table1");
myDs.Tables.Add(myDt);
myDt.Columns.Add("Size", typeof(string));
myDt.Columns.Add("Ranking", typeof(int), "Iif((Size)='Large', 1, Iif((Size)='Medium', 2, Iif((Size)='Small', 3, 4)))");
DataRow myDr1 = myDs.Tables["Table1"].NewRow();
DataRow myDr2 = myDs.Tables["Table1"].NewRow();
DataRow myDr3 = myDs.Tables["Table1"].NewRow();
DataRow myDr4 = myDs.Tables["Table1"].NewRow();
myDr1["Size"] = "Large";
myDr2["Size"] = "Medium";
myDr3["Size"] = "Small";
myDr4["Size"] = "Large";
myDt.Rows.Add(myDr1);
myDt.Rows.Add(myDr2);
myDt.Rows.Add(myDr3);
myDt.Rows.Add(myDr4);
DataView myDv = new DataView(myDt);
myDv.Sort = "Ranking";
ultraGrid1.DataSource = myDv;
And that is the code that proves it.
Map big, medium, small to an enum in the correct order and sort on that.
Any other answer will require more information as asked for in the comments.
If this is really as masqueraded T-SQL question, you could either use UNION (as proposed by Lukasz), or you could use a few WHEN ... THEN constructs in your ORDER BY:
SELECT *
FROM SomeTable
ORDER BY WHEN size = 'big' THEN 2 WHEN 'medium' THEN 1 ELSE 0 END
This might also work in ADO.NET, but I have not tested it:
myDataTable.DefaultView.Sort =
"WHEN size = 'big' THEN 2 WHEN 'medium' THEN 1 ELSE 0 END";
EDIT: David points out, that in your specific case (big, medium, small), the alphabetical sort order matches the expected ordering. In this special case, you can simply do:
ORDER BY size
Or in ADO.NET:
myDataTable.DefaultView.Sort = "size";
If you want a simple solution (and if these values are unlikely to change), just order by the 'size' column, since the big, medium and small words are in alphabetical order :D
Using the built-in sorting algorithm of the DataView (which is where you'd do actual sorting, not in the DataTable itself), this isn't possible, strictly speaking; there is no support for "custom" sorting, which is what you'd need. The sorting capabilities of the DataView are actually surprisingly limited, and (IIRC) it isn't even a stable sort (since it uses Array.Sort, which is documented as unstable).
Your only real option (from a GUI perspective) is to use a data display control that provides custom sorting capability, like the DevExpress XtraGrid.
After edits by OP, it has become (somewhat) clear that we are dealing with a DataTable.
One approach to sorting the rows of a DataTable according to a "mapped value", would be to use its Select method to retrieve several arrays (one for each value) of rows:
List<DataRow> rows = new List<DataRow>();
rows.AddRange(oldTable.Select("size = 'Big'"));
rows.AddRange(oldTable.Select("size = 'Medium'"));
rows.AddRange(oldTable.Select("size = 'Small'"));
If needed, the rows can be imported into a fresh DataTable with the same schema:
DataTable newTable = oldTable.Clone();
foreach (DataRow row in rows)
{
newTable.ImportRow(row);
}
This approach is definitely not very efficient, but it's probably about the easiest way to do this.
Try a switch statement.
Switch(var)
{ Case "Big":
//Append to TextBox1
//Or add to var1[]
break;
Case "Small":
//Append to TextBox2
//Or add to var2[]
break;
Case "Medium":
//Append to TextBox3
//Or add to var3[]
break;
}
EDIT: Sort into txtbox's or var[]'s, does this go-round make more sense?
Related
Is it possible to change order of rows in DataTable so for example the one with current index of 5 moves to place with index of 3, etc.?
I have this legacy, messy code where dropdown menu get it's values from DataTable, which get it's values from database. It is impossible to make changes in database, since it has too many columns and entries. My original though was to add new column in db and order by it's values, but this is going to be hard.
So since this is only matter of presentation to user I was thinking to just switch order of rows in that DataTable. Does someone knows the best way to do this in C#?
This is my current code:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
foreach (DataRow row in result.Rows)
{
m_cboReasonCode.Properties.Items.Add(row["FLOKKUR"].ToString().Trim() + " - " + row["SKYRING"]);
}
For example I want to push row 2011 - Credit previously issued to the top of the DataTable.
SOLUTION:
For those who might have problems with ordering rows in DataTable and working with obsolete technology that doesn't supports Linq this might help:
DataRow firstSelectedRow = result.Rows[6];
DataRow firstNewRow = result.NewRow();
firstNewRow.ItemArray = firstSelectedRow.ItemArray; // copy data
result.Rows.Remove(firstSelectedRow);
result.Rows.InsertAt(firstNewRow, 0);
You have to clone row, remove it and insert it again with a new index. This code moves row with index 6 to first place in the DataTable.
If you really want randomness you could use Guid.NewGuid in LINQ's OrderBy:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
var randomOrder = result.AsEnumerable().OrderBy(r => Guid.NewGuid());
foreach (DataRow row in randomOrder)
{
// ...
}
If you actually don't want randomness but you want specific values at the top, you can use:
var orderFlokkur2011 = result.AsEnumerable()
.OrderBy(r => r.Field<int>("FLOKKUR") == 2011 ? 0 : 1);
You can use linq to order rows:
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
foreach (DataRow row in result.Rows.OrderBy(x => x.ColumnName))
{
m_cboReasonCode.Properties.Items.Add(row["FLOKKUR"].ToString().Trim() + " - " + row["SKYRING"]);
}
To order by multiple columns:
result.Rows.OrderBy(x => x.ColumnName).ThenBy(x => x.OtherColumnName).ThenBy(x.YetAnotherOne)
To order by a specific value:
result.Rows.OrderBy(x => (x.ColumnName == 2001 or x.ColumnName == 2002) ? 0 : 1).ThenBy(x => x.ColumName)
You can use the above code to "pin" certain rows to the top, if you want more granular than that you can use a switch for example to sort specific values into sorted values of 1, 2, 3, 4 and use a higher number for the rest.
You can not change the order or delete a row in a foreach loop, you should create a new datatable and randomly add the rows to new datatable, you should also track the inserted rows not to duplicate
Use a DataView
DataTable result = flokkurDao.GetMCCHABAKflokka("MSCODE");
DateView view = new DateView(result);
view.Sort = "FLOKKUR";
view.Filter = "... you can even apply an in memory filter here ..."
foreach (DataRowView row in view.Rows)
{
....
Every data table comes with a view DefaultView which you can use, this way you can apply the default sorting / filtering in your datalayer.
public DataTable GetMCCHABAKflokka(string tableName, string sort, string filter)
{
var result = GetMCCHABAKflokka(tableName);
result.DefaultView.Sort = sort;
result.DefaultView.Filter = filter;
return result;
}
// use like this
foreach (DataRowView row in result.DefaultView)
I made the following code to add external data table to another table in MS word document, its working fine but takes a lot of time in case that the number of rows is more than 100, and in case of adding table with rows count more that 500 it fills the ms word table really slow and can't complete the task.
I tried to hide the document and disable the screen update for the document but still no solution for the slow performance.
//Get the required external data to the DT data table
DataTable DT = XDt.GetData();
Word.Table TB;
int X = 1;
foreach (DataRow Rw in DT.Rows)
{
Word.Row Rn = TB.Rows.Add(TB.Rows[X + 1]);
for(int i=0;i<=DT.Columns.Count-1;i++)
{
Rn.Cells[i+1].Range.Text = Rw[i].ToString());
}
X++;
}
So is there a way to make this process go faster ?
The most efficient way to add a table to Word is to first concatenate the data in a delimited text string, where "/n" must be the symbol for end-of-row (record separator). The end-of-cell (field separator) can be any character you like that's not in the string content that makes up the table.
Assign this string to a Range object, then use the ConvertToTable() method to create the table.
You're retrieving the last row of the current table for the BeforeRow parameter of TB.Rows.Add. This is significantly slower than simply adding the row. You should replace this:
Word.Row Rn = TB.Rows.Add(TB.Rows[X + 1]);
With this:
Word.Row Rn = TB.Rows.Add();
Utilizing parallelization as suggested in the comments might help slightly, but I'm afraid it's not going to do much good seeing the table add code runs on the main thread as mentioned in this link.
EDIT:
If performance is still an issue, I'd look into creating the Word table independently of the Word object model by using OpenXML. It's orders of magnitude faster.
ConvertToTable method is orders of magnitude faster than adding Rows/Cells one at a time.
while (reader.Read())
{
values = new object[reader.FieldCount];
var cols = reader.GetValues(values);
var item = String.Join("\t", values);
items.Add(item);
};
data = String.Join("\n", items.ToArray());
var tempDocument = application.Documents.Add();
var range = tempDocument.Range();
range.Text = data;
var tempTable = range.ConvertToTable(Separator: Microsoft.Office.Interop.Word.WdTableFieldSeparator.wdSeparateByTabs,
NumColumns: reader.FieldCount,
NumRows: rows, DefaultTableBehavior: WdDefaultTableBehavior.wdWord9TableBehavior,
AutoFitBehavior: WdAutoFitBehavior.wdAutoFitWindow);
I need to know the original type of each column in my DataGridView.
It is bound with a dynamic SQL select, say, "SELECT * FROM artists;"
I want to add a form view of the data above the grid and am programmtically creating Labels and TextBoxes and then some to hold the fields. I add them to a FlowLayoutPanel but I would like to adapt the sizes, especially the multiline property and the height to accomodate long comment and description fields of say 200-500 characters.
All I found when looking into the text columns was datatype string.
I know I can look up the columns by querying the systables, but it would be nice to find the original datatype a bit closer than that; also I'm using MYSQL atm, and a solution that doesn't need to query the database would hopefully also be DBMS independent.
Edit 1
I fill the DGV with nothing fancy:
DBDA = new MySqlDataAdapter(sql, DBC);
MySqlCommandBuilder cb = new MySqlCommandBuilder(DBDA);
DBDS = new DataSet(ddlb_tables.Text);
DBDA.FillSchema(DBDS, SchemaType.Mapped); //<- This was the missing piece of code!!
DBDA.Fill(DBDS, ddlb_tables.Text);
dataGridView1.DataSource = DBDS;
dataGridView1.DataMember = ddlb_tables.Text;
Edit 2
With the help of the accepted answer (DBDA.MissingSchemaAction) I could solve my problem. Here is the resulting function in its first, raw version:
public int getColumnSize(DataGridViewColumn dc)
{
try
{
DataGridView DGV = dc.DataGridView;
DataSet DS = (DataSet)DGV.DataSource;
DataTable DT = DS.Tables[0];
DataColumn DC = DT.Columns[dc.Name];
return DC.MaxLength;
} catch { }
return -1;
}
Not getting the original type is not a problem, as long as I know the length of the fields.
use the DataSet instead DataGrid:
foreach (DataColumn col in DBDS[ddlb_tables.Text].Text.Columns)
{
if (col.DataType == typeof(string))
{
var len = col.MaxLenght;
...
}
}
EDIT:
You may need to add the following line before filling:
DBDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
Source: The DataAdapter.Fill method does not set all of the properties of the DataTable and DataColumn objects
EDIT 2:
Or for result sets without a key:
DBDA.FillSchema(DBDS, SchemaType.Source);
Or:
DBDA.FillSchema(DBDS, SchemaType.Mapped);
I have now a problem with a very old system of ours. (!It is more then 7 years old and I have no budget and resources to make bigger change in the structure, so the decision to improve the old logic as many as we can.!)
We have an own written gridcontrol. Basically it is like a normal ASP.NET grid, you can add, change, delete elements.
The problem is that the grid has a BindGrid() method, where for further usage, the rows of the datasource table copied into a DataRow[]. I need to keep the DataRow[], but I would like to implement the best way to copy the source from the the table into the array.
The current solution:
DataRow[] rows = DataSource.Select("1=1", SortOrderString);
As I experienced so far, if I need to get a specified sort, that could be the best way (I'm also interested if it has a quicker way or not.)
BUT there are some simplified pages, where the SortOrder is not needed.
So I could make two method one for the sort order and one for without.
The real problem is the second one:
DataRow[] rows = DataSource.Select("1=1");
Because it is very slow. I made some test and it is kind of 15 times slower then the CopyTo() solution:
DataRow[] rows = new DataRow[DataSource.Rows.Count];
DataSource.Rows.CopyTo(rows,0);
I would like to use the faster way, BUT when I made the tests some old function simply crashed. It seems, there is an other difference, what I only noticed now:
The Select() gets the rows like the RowChanges are accepted.
So if I deleted a row, and I do not use the AcceptRowChanges() (I can't do that unfortunately), then with Select("1=1") the row is in the DataSource but not in the DataRow[].
With a simple .CopyTo() the row is there, and that is a bad news for me.
My questions are:
1) Is the Select("1=1") the best way to get the rows by the RowChanges? (I doubt a bit, because it is like 6 year old part)
2) And if 1) is not, is it possible to achieve a faster way with the same result than the .Select("1=1") ?
UPDATE:
Here is a very basic test app, what I used for speedtesting:
DataTable dt = new DataTable("Test");
dt.Columns.Add("Id", typeof (int));
dt.Columns.Add("Name", typeof(string));
for (int i = 0; i < 10000; i++)
{
DataRow row = dt.NewRow();
row["ID"] = i;
row["Name"] = "Name" + i;
dt.Rows.Add(row);
}
dt.AcceptChanges();
DateTime start = DateTime.Now;
DataRow[] rows = dt.Select();
/*DataRow[] rows = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(rows,0);*/
Console.WriteLine(DateTime.Now - start);
You can call Select without an argument: DataRow[] allRows = DataSource.Select(); That would be for sure more efficient than "1=1" since that applies a pointless RowFilter.
Another way is using Linq-To-DataSet to order and filter the DataTable. That isn't more efficient but more readable and maintainable.
I have yet no example or measurement, but it is obvious that a RowFilter with "1=1" is more expensive than none. Select is implemented in this way:
public Select(DataTable table, string filterExpression, string sort, DataViewRowState recordStates)
{
this.table = table;
this.IndexFields = table.ParseSortString(sort);
this.indexDesc = Select.ConvertIndexFieldtoIndexDesc(this.IndexFields);
// following would be omitted if you would use DataSource.Select() without "1=1"
if (filterExpression != null && filterExpression.Length > 0)
{
this.rowFilter = new DataExpression(this.table, filterExpression);
this.expression = this.rowFilter.ExpressionNode;
}
this.recordStates = recordStates;
}
If you want to be able to select also the rows that are currently not accepted, you can use the overload of Select:
DataRow[] allRows = DataSource.Select("", "", DataViewRowState.CurrentRows | DataViewRowState.Deleted);
This will select all rows inclusive the rows that are deleted even if AcceptChanges was not called yet.
I have a DataSet which I get a DataTable from that I am being passed back from a function call. It has 15-20 columns, however I only want 10 columns of the data.
Is there a way to remove those columns that I don't want, copy the DataTable to another that has only the columns defined that I want or is it just better to iterate the collection and just use the columns I need.
I need to write the values out to a fixed length data file.
Aside from limiting the columns selected to reduce bandwidth and memory:
DataTable t;
t.Columns.Remove("columnName");
t.Columns.RemoveAt(columnIndex);
To remove all columns after the one you want, below code should work. It will remove at index 10 (remember Columns are 0 based), until the Column count is 10 or less.
DataTable dt;
int desiredSize = 10;
while (dt.Columns.Count > desiredSize)
{
dt.Columns.RemoveAt(desiredSize);
}
The question has already been marked as answered, But I guess the question states that the person wants to remove multiple columns from a DataTable.
So for that, here is what I did, when I came across the same problem.
string[] ColumnsToBeDeleted = { "col1", "col2", "col3", "col4" };
foreach (string ColName in ColumnsToBeDeleted)
{
if (dt.Columns.Contains(ColName))
dt.Columns.Remove(ColName);
}
How about you just select the columns you want like this:
Dim Subjects As String = "Math, English"
Dim SubjectData As DataTable = Table.AsDataView.ToTable(True, Subjects.Split(","))