I have a DataTable which has the following structure.
StartDate (Type DateTime)
EndDate (Type DateTime)
Description (Type Text)
What I need to check is if the dates overlap or not, i.e., if the Start Date of 1 item is less than the EndDate of the previous item.
Here is the code I wrote.
NB: I already have checks to ensure that for each record, the Start Date has to be lesser than the End Date
dtDates.DefaultView.Sort = "StartDate ASC"; //Sort the DataTable based on the Start Dates
if(dtDates.Rows.Count > 1) //Check only if more than 1 Date is specified
{
for (int i = 1; i < dtDates.Rows.Count; i++)
{
if(TypeConvert.ToDate(dtDates.Rows[i]["StartDate"] < TypeConvert.ToDate(dtDates.Rows[i-1]["EndDate"])
{
retVal = true;
currentRow = i;
break;
}
}
}
The above code works fine.
But now, I want to implement it using LINQ
So, here is the code I am trying to attempt.
How can I do the whole comparison using LINQ?
public static bool CheckIfDatesOverlap(DataTable dataTable)
{
bool retVal = false;
int currentRow = 0;
List<DataRow> listDates = (from DataRow dgv in dataTable.Rows
select dgv).OrderBy(x => x[StartDate]).ToList();
if (listDates.Count > 1) //Perform Comparision only if more than 1 row is present
{
for (int i = 1; i < listDates.Count; i++)
{
if (TypeConvert.ToDateTime(listDates[i][StartDate]) < TypeConvert.ToDateTime(listDates[i][EndDate]))
{
retVal = true; //There are duplicates, hence return true
currentRow = i;
break;
}
else
{
//Do nothing as dates do not overlap
}
}
}
else
{
retVal = false; //As there is only 1 row, return false
}
if (retVal)
{
string message = "Dates Overlap";
//Display message
}
return retVal;
}
If you use SelectWithPrevious described here then you can write the following:
bool datesOverlap = table.Rows
.Cast<DataRow>()
.SelectWithPrevious((current, previous) => new
{
PreviousEndDate = (DateTime)previous["EndDate"],
StartDate = (DateTime)current["StartDate"]
})
.Any(x => x.StartDate < x.PreviousEndDate);
Related
I need to perform the sum of each field across multiple records of the same entity and update the values on the same entity. Along with this I also need to store its formula.
AttributeList = { "price ", "quantity", "contact.revenue", "opportunity.sales"}
Below is the logic
foreach (var attribute in attributeList)
{
Decimal fieldSum = 0;
string computedNote = string.Empty;
foreach (var entity in mainEntityList)
{
if (entity.Contains(attribute))
{
if (entity.Attributes[attribute] != null)
{
string type = entity.Attributes[attribute].GetType().Name;
Decimal attrValue = 0;
if (type == "AliasedValue")
{
AliasedValue aliasedFieldValue = (entity.GetAttributeValue<AliasedValue>(attribute));
attrValue = aliasedFieldValue.Value.GetType().Name == "Decimal" ? (Decimal)aliasedFieldValue.Value : (Int32)aliasedFieldValue.Value;
}
else
{
attrValue = entity.Attributes[attribute].GetType().Name == "Decimal" ? entity.GetAttributeValue<Decimal>(attribute) : entity.GetAttributeValue<Int32>(attribute);
}
fieldSum += attrValue;
computedNote += $"+{Convert.ToInt32(attrValue).ToString()}";
}
}
else
{
computedNote += $"+0";
}
}
Entity formula = new Entity("formula");
if (fieldSum != 0)
{
if (attribute.Contains("opportunity"))
{
opportunity[attributeName] = fieldSum;
entityName = Opportunity.EntityLogicalName;
attributeName = attribute;
recordId = Id;
}
else if (attribute.Contains("contact"))
{
contact[attributeName] = fieldSum;
entityName = Contact.EntityLogicalName;
attributeName = attribute;
recordId = Id;
}
else
{
mainentity[attribute] = fieldSum;
entityName = mainEntity.EntityLogicalName;
attributeName = attribute;
recordId = Id;
}
formula.Attributes["ice_entity"] = entityName;
formula.Attributes["ice_attribute"] = attributeName;
formula.Attributes[entityName + "id"] = new EntityReference(entityName, recordId);
formula.Attributes["ice_computednote"] = computedNote.Remove(0, 1);
requestsCollection.Entities.Add(formula);
}
}
requestsCollection.Entities.Add(opportunity);
requestsCollection.Entities.Add(contact);
requestsCollection.Entities.Add(mainentity);
Values in both records could be as follows
Record 1
Price = 500
Quantity = 25
Revenue = 100
Sales = 10000
Volume = 0
Record 2
Price = 200
Quantity = 10
Revenue = 100
Sales = -10000
Volume = 0
Record 3 (Values after calculation that are to be updated in the third entity and Formula to be stored mentioned in brackets)
Price = 700 Formula = (500+200)
Quantity = 35 Formula = (25+10)
Revenue = 200 Formula =(100+100)
Sales = 0 Formula =(10000 + (-10000))
Volume = 0 No Formula to be created
I am checking if the fieldsum is not equal to zero (to update both positive and negative values) and then updating the values in the respective entity. However for values that became zero after the calculation. I also need to update them and create formula for the same. Avoiding the values that were zero by default.
As shown in above example, I want to update sales field value and create formula record for the same as '10000+-10000' but do not want volume field value to be updated or the formula to be created for it. How can i embed this logic in my code?
Add a flag (updateFormula) to indicate whether checksum and formula required to update in related entities. Then, instead of checking fieldSum != 0, check updateFormula is true to update the related records.
attributeList = { "price", "quantity", "contact.revenue", "opportunity.sales"}
foreach (var attribute in attributeList)
{
Decimal fieldSum = 0;
string computedNote = string.Empty;
bool updateFormula = false;
foreach (var entity in mainEntityList)
{
if (entity.Contains(attribute))
{
if (entity.Attributes[attribute] != null)
{
string type = entity.Attributes[attribute].GetType().Name;
Decimal attrValue = 0;
if (type == "AliasedValue")
{
AliasedValue aliasedFieldValue = (entity.GetAttributeValue<AliasedValue>(attribute));
attrValue = aliasedFieldValue.Value.GetType().Name == "Decimal" ? (Decimal)aliasedFieldValue.Value : (Int32)aliasedFieldValue.Value;
}
else
{
attrValue = entity.Attributes[attribute].GetType().Name == "Decimal" ? entity.GetAttributeValue<Decimal>(attribute) : entity.GetAttributeValue<Int32>(attribute);
}
fieldSum += attrValue;
computedNote += Convert.ToInt32(attrValue).ToString();
updateFormula = true;
}
}
else
{
computedNote += 0;
}
}
Entity formula = new Entity("formula");
if (updateFormula)
{
// Logic to update formula and checksum
}
}
Im trying to write a method which will allow me to search different DataTables, over different columns.
So far i have the following:
string selectedValue;
string searchColumn;
string targetColumn;
var results = (from a in dt.AsEnumerable()
where a.Field<string>(searchColumn) == selectedValue
select new
{
targetColumn = a.Field<string>(targetColumn)
}).Distinct();
Which kind of gets the job done, but I'm left with the column name as targetColumn rather than the actual column name I want.
Is there any way to resolve this?
Thanks in advance
CM
I make a LINQ to Datatables
public List<DataRow> Where(this DataTable dt, Func<DataRow, bool> pred)
{
List<DataRow> res = new List<DataRow>();
try {
if (dt != null && dt.Rows.Count > 0) {
for (i = 0; i <= dt.Rows.Count - 1; i++) {
if (pred(dt(i))) {
res.Add(dt(i));
}
}
}
} catch (Exception ex) {
PromptMsg(ex);
}
return res;
}
Usage :
var RowsList = dt.Where(f => f("SomeField").toString() == "SomeValue" ||
f("OtherField") > 5);
Suppose we've a grid with sortable columns.
If a user clicks on a column, it gets sorted by 'asc', then if a user clicks the column header again, it gets sorted by 'desc', now I want similar functionality when a user clicks the column third time, the sorting is removed, i.e. returned to previous style, the css is changed back to normal/non-italic etc?
Today I was trying to achieve same thing. I've skimmed through SlickGrid code but didn't find any 'Reset' function. So, I've tweaked the slick.grid.js v2.2 a little bit.
Basically you just need to add a new array - 'latestSortColumns' which will store sort columns state (deep copy of internal SlickGrid sortColumns array).
var latestSortColumns = [];
Optionally add new setting to opt-in for sort reset.
var defaults = {
(...),
autoResetColumnSort: false
};
Change setupColumnSort to reset sort after third click on column's header.
function setupColumnSort() {
$headers.click(function (e) {
// temporary workaround for a bug in jQuery 1.7.1 (http://bugs.jquery.com/ticket/11328)
e.metaKey = e.metaKey || e.ctrlKey;
if ($(e.target).hasClass("slick-resizable-handle")) {
return;
}
var $col = $(e.target).closest(".slick-header-column");
if (!$col.length) {
return;
}
var column = $col.data("column");
if (column.sortable) {
if (!getEditorLock().commitCurrentEdit()) {
return;
}
var sortOpts = null;
var i = 0;
for (; i < sortColumns.length; i++) {
if (sortColumns[i].columnId == column.id) {
sortOpts = sortColumns[i];
sortOpts.sortAsc = !sortOpts.sortAsc;
break;
}
}
**if ((e.metaKey || (options.autoResetColumnSort && latestSortColumns[i] != null && latestSortColumns[i].sortAsc === !column.defaultSortAsc)) && options.multiColumnSort) {**
if (sortOpts) {
sortColumns.splice(i, 1);
**latestSortColumns.splice(i, 1);**
}
}
else {
if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) {
sortColumns = [];
}
if (!sortOpts) {
sortOpts = { columnId: column.id, sortAsc: column.defaultSortAsc };
sortColumns.push(sortOpts);
} else if (sortColumns.length == 0) {
sortColumns.push(sortOpts);
}
}
setSortColumns(sortColumns);
if (!options.multiColumnSort) {
trigger(self.onSort, {
multiColumnSort: false,
sortCol: column,
sortAsc: sortOpts.sortAsc}, e);
} else {
trigger(self.onSort, {
multiColumnSort: true,
sortCols: $.map(sortColumns, function(col) {
return {sortCol: columns[getColumnIndex(col.columnId)], sortAsc: col.sortAsc };
})}, e);
}
}
});
}
Store new state after every sort change in latestSortColumns:
function setSortColumns(cols) {
sortColumns = cols;
var headerColumnEls = $headers.children();
headerColumnEls
.removeClass("slick-header-column-sorted")
.find(".slick-sort-indicator")
.removeClass("slick-sort-indicator-asc slick-sort-indicator-desc");
$.each(sortColumns, function(i, col) {
if (col.sortAsc == null) {
col.sortAsc = true;
}
var columnIndex = getColumnIndex(col.columnId);
if (columnIndex != null) {
headerColumnEls.eq(columnIndex)
.addClass("slick-header-column-sorted")
.find(".slick-sort-indicator")
.addClass(col.sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc");
}
});
**for (var i = 0; i < sortColumns.length; i++)
latestSortColumns[i] = { columnId: sortColumns[i].columnId, sortAsc: sortColumns[i].sortAsc };**
}
That's all, should work now.
This is a function I call to reset all columns back to their original order.
It requires one of columns to be set up as not sortable.
In the example below I use the first column of the table, columns[0], which has the field id "locus".
function removeSorting() {
columns[0].sortable = true;
$('.slick-header-columns').children().eq(0).trigger('click');
columns[0].sortable = false;
// clear other sort columns
grid.setSortColumns( new Array() );
}
Then in the typical dataView.sort() function you make an exception for this column:
grid.onSort.subscribe(function(e,args) {
var cols = args.sortCols;
dataView.sort(function (dataRow1, dataRow2) {
for( var i = 0; i < cols.length; i++ ) {
var field = cols[i].sortCol.field;
// reset sorting to original indexing
if( field === 'locus' ) {
return (dataRow1.id > dataRow2.id) ? 1 : -1;
}
var value1 = dataRow1[field];
var value2 = dataRow2[field];
if( value1 == value2 ) continue;
var sign = cols[i].sortAsc ? 1 : -1;
return (value1 > value2) ? sign : -sign;
}
return 0;
});
});
Will the table always be sorted in some order on some column?
If not then you'll have to store a lot of state the first time a column is clicked just in case you want to restore it after a third click.
I have created a JTable, the table contains 4 rows, and for a specific row, I have overridden the sorting and its working fine only on that column.
Status Scheduled Date Scheduled time Status
false 30/01/2012 02:00:00 Scheduled
false 29/01/2012 14:58:00 Scheduled
false 29/01/2012 15:50:00 Scheduled
For Scheduled Date, which I try to sort, it would sort, but the respecitve rows are not being updated.
Here is my code for sorting
public static void sortColumn(DefaultTableModel model, int colIndex,
boolean sortingOrder) {
Vector<?> data = model.getDataVector();
Object[] colData = new Object[model.getRowCount()];
SortedSet<Object> dataCollected = null;
List<Date> dateCollected;
boolean dateFlag = false;
dateCollected = new ArrayList<Date>();
// Copy the column data in an array
for (int i = 0; i < colData.length; i++) {
Object tempData = ((Vector<?>) data.get(i)).get(colIndex);
if ((colIndex == 1 || colIndex == 4)
&& tempData.toString().contains("/")) {
String[] _scheduledDate1 = ((String) tempData).split("/");
Calendar _cal1 = Calendar.getInstance();
_cal1.set(Integer.parseInt(_scheduledDate1[2]),
Integer.parseInt(_scheduledDate1[1]) - 1,
Integer.parseInt(_scheduledDate1[0]));
dateCollected.add(_cal1.getTime());
dateFlag = true;
} else {
colData[i] = ((Vector<?>) data.get(i)).get(colIndex);
}
}
// DateCompare compare = new DateCompare();
if (!dateFlag) {
dataCollected = new TreeSet<Object>();
dataCollected.add(colData);
dateFlag = false;
}
// Copy the sorted values back into the table model
if ((colIndex == 1 || colIndex == 4) && dateFlag) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sortOrder = !sortOrder;
if (sortOrder) {
Collections.sort(dateCollected);
} else {
Collections.sort(dateCollected, Collections.reverseOrder());
}
colData = dateCollected.toArray();
for (int i = 0; i < colData.length; i++) {
((Vector<Object>) data.get(i)).set(colIndex,
sdf.format(((Date) colData[i]).getTime()));
}
} else {
for (int i = 0; i < colData.length; i++) {
((Vector<Object>) data.get(i)).set(colIndex, colData[i]);
}
}
model.fireTableStructureChanged();
}
How to I get the entire row update accordingly?
I found the problem, my object against I was comparing was wrong, I've change the code for the same it all works fine.
I implemented QuickSort algorithm to sort the vector on the specific column I need.
I´m exporting datatables to Excel workbook. Problem is that the datatable holds 90000 rows and excel can only hold 67000 rows in every sheet.
So..
How can i divide one big datatable to two datatables, maybe with Linq ?
Then i can have datatable1 in sheet1 and datatable2 in sheet2
Sincerly
agh
Assuming that you're getting the 90,000 rows for this DataTable from a database somewhere, the most efficient approach would be to modify your SELECT statement into two new SELECT statements, each of which returns < 67,000 rows, and then do everything else the same.
Split your recordset. Perform one SELECT that extracts all 90,000 rows, and split it on Excel import step.
private List<DataTable> CloneTable(DataTable tableToClone, int countLimit)//Split function
{
List<DataTable> tables = new List<DataTable>();
int count = 0;
DataTable copyTable = null;
foreach (DataRow dr in tableToClone.Rows)
{
if ((count++ % countLimit) == 0)
{
copyTable = new DataTable();
copyTable = tableToClone.Clone();
copyTable.TableName = "Sample" + count;
tables.Add(copyTable);
}
copyTable.ImportRow(dr);
}
return tables;
}
protected void LinkReport_Click(object sender, EventArgs e)
{
DataTable dt2 = (DataTable)ViewState["dtab"];
List<DataTable> dt1 = CloneTable(dt2, 5);
DataSet ds = new DataSet("dst");
for (int i = 0; i < dt1.Count; i++)
{
ds.Tables.Add(dt1[i]);
}
string filePath = Server.MapPath("Reports/").ToString() + "master.xls";
FileInfo file = new FileInfo(filePath);
if (file.Exists)
{
file.Delete();
}
Export(ds, filePath);// Export into Excel
}
Clone - The fastest method to create tables with original columns structure is Clone method.
Export into Excel
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
}
finally
{
GC.Collect();
}
}
public void Export(DataSet ds, string filePath)
{
string data = null;
string columnName = null;
int i = 0;
int j = 0;
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
//Excel.Worksheet xlWorkSheet;
Excel.Worksheet xlWorkSheet = null;
object misValue = System.Reflection.Missing.Value;
Excel.Range range;
xlApp = new Excel.ApplicationClass();
xlWorkBook = xlApp.Workbooks.Add(misValue);
//xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
for (int l = 0; l < ds.Tables.Count; l++)
{
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(l + 1);
xlWorkSheet.Cells[1, 1] = "Report";
xlWorkSheet.get_Range("A1:D1", Type.Missing).Merge(Type.Missing);
xlWorkSheet.get_Range("A1", "D1").Font.Bold = true;
xlWorkSheet.Cells.Font.Name = "Courier New";
if (l == 0)
{
xlWorkSheet.Name = "Sheet1";
}
else if (l == 1)
{
xlWorkSheet.Name = "Sheet2";
}
else if (l == 2)
{
xlWorkSheet.Name = "Sheet3";
}
else if (l == 3)
{
xlWorkSheet.Name = "Sheet4";
}
else if (l == 4)
{
xlWorkSheet.Name = "Sheet5";
}
for (i = 0; i <= ds.Tables[l].Rows.Count - 1; i++)
{
for (j = 0; j <= ds.Tables[l].Columns.Count - 1; j++)
{
columnName = ds.Tables[l].Columns[j].ColumnName.ToString();
xlWorkSheet.Cells[3, j + 1] = columnName;
data = ds.Tables[l].Rows[i].ItemArray[j].ToString();
xlWorkSheet.Cells[i + 5, j + 1] = data;
}
}
}
//for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
//{
// for (j = 0; j <= ds.Tables[0].Columns.Count - 1; j++)
// {
// data = ds.Tables[0].Rows[i].ItemArray[j].ToString();
// xlWorkSheet1.Cells[i + 1, j + 1] = data;
// }
//}
xlWorkBook.SaveAs(filePath, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
xlWorkBook.Close(true, misValue, misValue);
xlApp.Quit();
// kill all excel processes
Process[] pros = Process.GetProcesses();
for (int p = 0; p < pros.Length; p++)
{
if (pros[p].ProcessName.ToLower().Contains("excel"))
{
pros[p].Kill();
break;
}
}
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);
}
Try this One.. I have Worked out in Visual Studio 2005
DataTable[] splittedtables = dt.AsEnumerable()
.Select((row, index) => new { row, index })
.GroupBy(x => x.index / Input From User) // integer division, the fractional part is truncated
.Select(g => g.Select(x => x.row).CopyToDataTable())
.ToArray();
This should work.