Rounding to 2 decimal points a DataTable based NuGet package - c#

I'm using ConsoleTableExt to print a tabulated table. I wanted to round a few decimals to 2 points. The NuGet package is using DataTable. Currently, I'm using .ToString(f2"), but I read a post which states that it's a bad practice to do that in a DataTable. Any suggestions?
// Main()
var tableBuilder = ConsoleTableBuilder.From(backtest.ReportResults(backtestResults)).WithFormat(ConsoleTableBuilderFormat.Alternative);
tableBuilder.ExportAndWriteLine();
public static DataTable ReportResults(List<BacktestResult> backtestResults)
{
var table = new DataTable();
table.Columns.Add("Pair", typeof(string));
table.Columns.Add("Trades", typeof(int));
table.Columns.Add("Average Profit %", typeof(string));
table.Columns.Add("Cumulative Profit %", typeof(string));
table.Columns.Add($"Total Profit", typeof(string));
table.Columns.Add($"Total Profit %", typeof(string));
foreach (var pair in _backtestOptions.Pairs)
{
var results = backtestResults.Where(e => e.Pair.Equals(pair)).ToList();
var trades = results.Count;
var profitMean = results.Count > 0 ? results.Average(e => e.ProfitPercentage) : 0;
var profitMeanPercentage = results.Count > 0 ? results.Average(e => e.ProfitPercentage) * 100 : 0;
var profitSum = results.Sum(e => e.ProfitPercentage);
var profitSumPercentage = results.Sum(e => e.ProfitPercentage) * 100;
var profitTotalAbs = results.Sum(e => e.ProfitAbs);
var profitTotal = results.Sum(e => e.ProfitAbs) / 1;
var profitTotalPercentage = results.Sum(e => e.ProfitPercentage) * 100;
table.Rows.Add(pair, trades, profitMeanPercentage.ToString("f2"), profitSumPercentage.ToString("f2"), profitTotalAbs.ToString("f8"),
profitTotalPercentage.ToString("f2"));
}
return table;
}

You can use the function Round of Math for round value. Round(double value, MidpointRounding mode) where the second parameter specification for how to round value if it is midway between two other numbers.
Example:
decimal decimalVal = 123.456M;
Math.Round(decimalVal, 2);

Related

Getting the sum per hour from 2 datatable

I'm writing a txt file from 2 data table.
Following is the 2 data table.
dt1
Transaction No. Time Amount Date
1 10:00:00 200.00 03/05/2020
2 10:30:11 250.00 03/05/2020
3 11:05:22 140.00 03/05/2020
4 11:45:33 230.00 03/05/2020
5 12:15:10 220.00 03/05/2020
dt2
Transaction No. Added Amount Date
1 40.00 03/05/2020
2 25.00 03/05/2020
3 40.00 03/05/2020
4 30.00 03/05/2020
5 30.00 03/05/2020
following is my code
using (StreamWriter sw = File.AppendText(fileName))
{
for (int a = 6; a <= 23; a++)
{
string aa = a.ToString().PadLeft(2, '0');
double salex = double.Parse(dt1.Rows[0]["Amount"].ToString());
if (salex.Equals(""))
{
salex = 0;
}
else
{
salex = double.Parse(dt1.Rows[0]["Amount"].ToString());
}
double vatx = double.Parse(dt2.Rows[0]["Added Amount"].ToString());
if (vatx.Equals(""))
{
vatx = 0;
}
else
{
vatx = double.Parse(dt2.Rows[0]["Added Amount"].ToString());
}
double dailysaleHRLY = -salex + -vatx;
sw.Write(dtpDate.Value.ToString("MM/dd/yyyy") + ",");
sw.Write(aa + ":00" + ",");
sw.Write(dailysaleHRLY.ToString("0.00") + ",");
}
for (int a = 0; a <= 5; a++)
{
string aa = a.ToString().PadLeft(2, '0');
double salex = double.Parse(dt1.Rows[0]["Amount"].ToString());
if (salex.Equals(""))
{
salex = 0;
}
else
{
salex = double.Parse(dt1.Rows[0]["Amount"].ToString());
}
double vatx = double.Parse(dt2.Rows[0]["Added Amount"].ToString());
if (vatx.Equals(""))
{
vatx = 0;
}
else
{
vatx = double.Parse(dt2.Rows[0]["Added Amount"].ToString());
}
double dailysaleHRLY = -salex + -vatx;
sw.Write(dtpDate.Value.ToString("MM/dd/yyyy") + ",");
sw.Write(aa + ":00" + ",");
sw.Write(dailysaleHRLY.ToString("0.00") + ",");
}
MessageBox.Show("Txt File succesfully created!", "SYSTEM", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
This is the output of my code.
Date, Time, Sum
03/05/2020,06:00,515.00
03/05/2020,07:00,515.00
03/05/2020,08:00,515.00
03/05/2020,09:00,515.00
03/05/2020,10:00,515.00
03/05/2020,11:00,515.00
03/05/2020,12:00,515.00
03/05/2020,13:00,515.00
03/05/2020,14:00,515.00
03/05/2020,15:00,515.00
03/05/2020,16:00,515.00
03/05/2020,17:00,515.00
03/05/2020,18:00,515.00
03/05/2020,19:00,515.00
03/05/2020,20:00,515.00
03/05/2020,21:00,515.00
03/05/2020,22:00,515.00
03/05/2020,23:00,515.00
03/05/2020,00:00,515.00
03/05/2020,01:00,515.00
03/05/2020,02:00,515.00
03/05/2020,03:00,515.00
03/05/2020,04:00,515.00
03/05/2020,05:00,515.00
I just want to get the sum of Amount and Added Amount base on hour. Like this.
Date, Time, Sum
03/05/2020,06:00,0.00
03/05/2020,07:00,0.00
03/05/2020,08:00,0.00
03/05/2020,09:00,0.00
03/05/2020,10:00,515.00
03/05/2020,11:00,440.00
03/05/2020,12:00,250.00
03/05/2020,13:00,0.00
03/05/2020,14:00,0.00
03/05/2020,15:00,0.00
03/05/2020,16:00,0.00
03/05/2020,17:00,0.00
03/05/2020,18:00,0.00
03/05/2020,19:00,0.00
03/05/2020,20:00,0.00
03/05/2020,21:00,0.00
03/05/2020,22:00,0.00
03/05/2020,23:00,0.00
03/05/2020,00:00,0.00
03/05/2020,01:00,0.00
03/05/2020,02:00,0.00
03/05/2020,03:00,0.00
03/05/2020,04:00,0.00
03/05/2020,05:00,0.00
Assuming that you have two DataTable-s and you have them filled with the mentioned data.
var dt1 = new DataTable();
var dt2 = new DataTable();
dt1.Columns.AddRange(new[]
{
new DataColumn("Transaction No.", typeof(int)),
new DataColumn("Time", typeof(DateTime)),
new DataColumn("Amount", typeof(decimal)),
new DataColumn("Date", typeof(DateTime)),
});
dt2.Columns.AddRange(new[]
{
new DataColumn("Transaction No.", typeof(int)),
new DataColumn("Added Amount", typeof(decimal)),
new DataColumn("Date", typeof(DateTime)),
});
Note: The double types have been replaced with decimal types since its the right type to be used when dealing with money.
As I understand the problem, you want to group the rows of dt1 by hour part of the Time field, sum the Amount, and add to the sum the Added Amount from dt2 rows where their Transaction No. equals to any Transaction No. of the grouped rows of dt1.
This will do:
var group = dt1.AsEnumerable().GroupBy(x => x.Field<DateTime>(1).Hour);
var sb = new StringBuilder();
sb.Append("Date,");
sb.Append("Time,".PadLeft(12, ' '));
sb.AppendLine("Sum".PadLeft(5, ' '));
//if PadLeft is not required in the output, then just:
//sb.AppendLine($"Date, Time, Sum");
foreach (var g in group)
{
var sum = 0M;
foreach (var r in g)
sum += r.Field<decimal>(2) + dt2.AsEnumerable()
.Where(x => x.Field<int>(0) == r.Field<int>(0))
.Sum(x => x.Field<decimal>(1));
sb.AppendLine($"{g.First().Field<DateTime>(3).ToString("MM/dd/yyyy")}, {g.Key.ToString("00")}:00, {sum.ToString("0.00")}");
}
Note: You can use the fields names instead of their indexes.
The output is:
Date, Time, Sum
03/05/2020, 10:00, 515.00
03/05/2020, 11:00, 440.00
03/05/2020, 12:00, 250.00
I don't know whether the DataTable-s already contain the required data to generate the output mentioned in the last quote block or you want to append the rest before writing to the text file. In case of the second scenario, you can do something like:
var group = dt1.AsEnumerable().GroupBy(x => x.Field<DateTime>(1).Hour);
var sb = new StringBuilder();
sb.AppendLine($"Date, Time, Sum");
for (var i = 0; i < 24; i++)
{
var g = group.FirstOrDefault(x => x.Key == i);
if (g != null)
{
var sum = 0M;
foreach (var r in g)
sum += r.Field<decimal>(2) + dt2.AsEnumerable()
.Where(x => x.Field<int>(0) == r.Field<int>(0))
.Sum(x => x.Field<decimal>(1));
sb.AppendLine($"{g.First().Field<DateTime>(3).ToString("MM/dd/yyyy")}, {g.Key.ToString("00")}:00, {sum.ToString("0.00")}");
}
else
sb.AppendLine($"{group.First().First().Field<DateTime>(3).ToString("MM/dd/yyyy")}, {i.ToString("00")}:00, 0.00");
}
If you need to preserve the same order of the hours:
for (var ii = 6; ii < 30; ii++)
{
var i = ii > 23 ? ii % 24 : ii;
var g = group.FirstOrDefault(x => x.Key == i);
if (g != null)
{
//The same...
}
Finally, to create or overwrite the text file (fileName):
File.WriteAllText(fileName, sb.ToString());
Or to append the output:
File.AppendAllText(fileName, sb.ToString());

Arithmetic operations on dictionary c#

I have this dictionary object as:- Dictionary<string,string> lstTransList;
This object has values as |Key={Id}|Value={|QTY=4|PICKEDUP=2}|
now I want to calculate count of records, difference of QTY & PICKEDUP for each record, then summation of QTY, summation PICKEDUP & summation of difference.
Is there any efficient way of performing these arithmatic operations using LINQ ?
I'm getting count as:- int total_transactiondone = lstTransList.Count();
for summation of QTY I want to split value of dictionary object using Keys as 'QTY' & 'PICKEDUP' but don't know how to use this. Any suggestions ??
Thinking of using something like this :
decimal total_tickets = lstTransList.Sum(x => Convert.ToDecimal(x.Value));
Edit :
Now I'm using followin approach to achieve this task. Is there any other efficient way to do this ? Please suggest.
var lstTransList = objRedis.GetAllEntriesFromHash(strHashey);
DataTable dtTransList = new DataTable();
dtTransList.Columns.Add("TransId");
dtTransList.Columns.Add("Qty", typeof(int));
dtTransList.Columns.Add("PickedUp", typeof(int));
dtTransList.Columns.Add("UnPickedUp", typeof(int));
foreach (KeyValuePair<string, string> entry in lstTransList)
{
DataRow DR = dtTransList.NewRow();
if (!string.IsNullOrEmpty(entry.Key))
{
DR[0] = entry.Key;
}
if (!string.IsNullOrEmpty(entry.Value))
{
clsKeyValueParser objKV = new clsKeyValueParser(entry.Value);
DR[1] = Convert.ToInt32(objKV.strGetValue("QTY", "0"));
DR[2] = Convert.ToInt32(objKV.strGetValue("PICKEDUP", "0"));
DR[3] = Convert.ToInt32(DR[1]) - Convert.ToInt32(DR[2]);
}
dtTransList.Rows.Add(DR);
}
int total_transactiondone = dtTransList.Rows.Count;
int total_tickets = dtTransList.AsEnumerable().Sum(x => x.Field<int>(1));
int total_ticketsunpickedup = dtTransList.AsEnumerable().Sum(x => x.Field<int>(3));
int total_pickeduptransaction = dtTransList.AsEnumerable().Count(row => row.Field<int>(3) == 0);
int total_unpickeduptransaction = dtTransList.AsEnumerable().Count(row => row.Field<int>(3) != 0);
Try this to get a total of the QTY
decimal total_tickets_qty = lstTransList.Sum(x => Convert.ToDecimal(Regex.Match(x.Value.Split('|')[1], #"\d+").Value);
Total of the PICKEDUP
decimal total_tickets_pickedup = lstTransList.Sum(x => Convert.ToDecimal(Regex.Match(x.Value.Split('|')[2], #"\d+").Value));
Total of the difference
decimal total_tickets = lstTransList.Sum(x => Convert.ToDecimal(Regex.Match(x.Value.Split('|')[1], #"\d+").Value) - Convert.ToDecimal(Regex.Match(x.Value.Split('|')[2], #"\d+").Value));

How to compare two datatables and only check for the rows found

var qry = from r1 in dtCMS.AsEnumerable() //row count - 416
from r2 in dtEcho.AsEnumerable() //row count - 175
where
r1.Field<string>("Name") == r2.Field<string>("Name")
select r2;
DataTable dt1 = dtEcho.AsEnumerable().Except(qry).CopyToDataTable();
Error:
The source contains no DataRows.
How can I modify the code above so that the comparison will only happen for the rows in r2?
I would like two functions:
One, that compare dtEcho to dtCMS, and return anything that is in
dtEcho but not in dtCMS.
Two, that compare dtEcho to dtCMS, and return anything that is in
dtCMS but not in dtEcho.
The code fails if the DataTable I am comparing against has more rows and hence it can't compare.
CopyToDataTable will throw an InvalidOperationException if the source sequence does not contain any DataRow objects:
The source contains no DataRows.
Your code is OK in general and to solve the problem with minimum changes, the only thing that you should change is the way that you create the result.
Instead of using CopyToDataTable put results in the result table using a for loop. To do so, first you should create a clone of your second table.
var qry = from r1 in table1.AsEnumerable() //row count - 416
from r2 in table2.AsEnumerable() //row count - 175
where
r1.Field<string>("Name") == r2.Field<string>("Name")
select r2;
var dt = table2.Clone();
table2.AsEnumerable().Except(qry).ToList().ForEach(x =>
{
dt.Rows.Add(x);
});
this.dataGridView1.DataSource = dt;
If I got right what you want, the code should look like this:
// setup
var dtCMS = new DataTable();
dtCMS.Columns.Add("ID", typeof(int));
dtCMS.Columns.Add("Name", typeof(String));
for (int i = 0; i < 416; i++)
dtCMS.Rows.Add(i, "Name " + i);
var dtEcho = new DataTable();
dtEcho.Columns.Add("ID", typeof(int));
dtEcho.Columns.Add("Name", typeof(String));
for (int i = 400; i < 575; i++)
dtEcho.Rows.Add(i, "Name " + i);
// convert to enumerables
var cmsEnum = dtCMS.AsEnumerable();
var echoEnum = dtEcho.AsEnumerable();
var CmsMinusEcho = cmsEnum.Where(cms => !echoEnum.Any(echo => echo.Field<String>("Name").Equals(cms.Field<String>("Name"))));
var EchoMinusCms = echoEnum.Where(echo => !cmsEnum.Any(cms => cms.Field<String>("Name").Equals(echo.Field<String>("Name"))));
// get DataTable with check
var CmsMinusEchoDt = CmsMinusEcho.Count() > 0 ? CmsMinusEcho.CopyToDataTable() : null;
var EchoMinusCmsDt = EchoMinusCms.Count() > 0 ? EchoMinusCms.CopyToDataTable() : null;

How to check a dataset belongs to another dataset

I have one dataset which contains Student Fee Structure of a single FeeID as mentioned below:
FeeID Amount FeeItem Type
*---------------------------------------------*
10 7500 Admission Fee T
10 900 Annual Fee T
10 150 Application Fee T
10 850 Boy's Uniform T
10 50 Computer Fee R
For example I have another dataset having following data:
FeeID Amount FeeItem Type
*---------------------------------------------*
9 8500 Admission Fee T
9 950 Annual Fee T
9 150 Application Fee T
9 850 Boy's Uniform T
9 50 Computer Fee R
11 7500 Admission Fee T
11 900 Annual Fee T
11 150 Application Fee T
11 850 Boy's Uniform T
11 50 Computer Fee R
I want to check whether the set comprising of last three columns belongs to another dataset that contains data of all Fee Structures where FeeID may vary. Actual I want to retrieve matching FeeID having same Fee Structure.
In the above example if I search for the first one in the second it will return True and matching FeeID will be 11.
You can use this trick
Dataset ds1
DataSet ds2
DataSet dest;
dest.Merge(ds1);
dest.AcceptChanges();
dest.Merge(ds2);
diff = destination.GetChanges()
and check if diff is empty
Edit: I've made ade some improvements to below code. Following DataTable.IndexOf(otherTable) extension method checks if a table is a subset of another table.
You can pass column names that you want to ignore in the comparison. The order of the columns does not matter but the order of DataRows(the subset must have the same order of rows).
It returns -1 when no equal subset of DataRows was found or the first index in the main-table(this in the extension) where the equal subset starts.
public static class DataTableExtensions
{
public static int IndexOf(this DataTable tblMain, DataTable tblSub, params String[] ignoreColumns)
{
if (tblMain == null)
throw new ArgumentNullException("tblMain");
if (tblSub.Rows.Count == 0)
throw new ArgumentException("tblSub must not be empty", "tblSub");
if (tblSub.Rows.Count > tblMain.Rows.Count)
return -1;
IEnumerable<String> relevantColumnNames = tblSub.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.Except(ignoreColumns)
.ToArray();
foreach (String colName in relevantColumnNames)
if (!tblMain.Columns.Contains(colName))
return -1;
for (int mainRowIndex = 0; tblMain.Rows.Count - mainRowIndex >= tblSub.Rows.Count; mainRowIndex++)
{
// check if current window is equal to tblSub
bool allRowsAreEqual = true;
for (int windowIndex = mainRowIndex; windowIndex < tblSub.Rows.Count + mainRowIndex; windowIndex++)
{
DataRow currentMain = tblMain.Rows[windowIndex];
DataRow currentSub = tblSub.Rows[windowIndex - mainRowIndex];
bool allFieldsAreEqual = relevantColumnNames.All(colName =>
Object.Equals(currentMain[colName], currentSub[colName]));
if (!allFieldsAreEqual)
{
allRowsAreEqual = false;
break; // continue with next window in main-table
}
}
if (allRowsAreEqual)
return mainRowIndex;
}
// no equal window found in main-table
return -1;
}
}
Your sample data:
var TblSub = new DataTable();
var TblMain = new DataTable();
TblSub.Columns.Add("FeeID", typeof(int));
TblSub.Columns.Add("Amount", typeof(int));
TblSub.Columns.Add("FeeItem", typeof(string));
TblSub.Columns.Add("Type", typeof(char));
TblMain.Columns.Add("FeeID", typeof(int));
TblMain.Columns.Add("Amount", typeof(int));
TblMain.Columns.Add("FeeItem", typeof(string));
TblMain.Columns.Add("Type", typeof(char));
TblSub.Rows.Add(10, 7500, "Admission Free", 'T');
TblSub.Rows.Add(10, 900, "Annual Fee", 'T');
TblSub.Rows.Add(10, 150, "Application Free", 'T');
TblSub.Rows.Add(10, 850, "Boy's Uniform", 'T');
TblSub.Rows.Add(10, 50, "Computer Free", 'R');
TblMain.Rows.Add(9, 8500, "Admission Free", 'T');
TblMain.Rows.Add(9, 950, "Annual Fee", 'T');
TblMain.Rows.Add(9, 150, "Application Free", 'T');
TblMain.Rows.Add(9, 850, "Boy's Uniform", 'T');
TblMain.Rows.Add(9, 50, "Computer Free", 'R');
TblMain.Rows.Add(10, 7500, "Admission Free", 'T');
TblMain.Rows.Add(11, 900, "Annual Fee", 'T');
TblMain.Rows.Add(11, 150, "Application Free", 'T');
TblMain.Rows.Add(11, 850, "Boy's Uniform", 'T');
TblMain.Rows.Add(11, 50, "Computer Free", 'R');
You can use it in this way:
int firstIndex = TblMain.IndexOf(TblSub, "FeeID");
if (firstIndex == -1)
Console.Write("Second table does not contain first table");
else
Console.Write("Second table does contain first table at row-index " + firstIndex);
Output is:
Second table does contain first table at row-index 5
old approach:
You could use following method which checks whether or not two DataTables are equal.
You need to add using System.Linq for Enumerable.SequenceEqual. If you cannot use Linq, use loops.
static bool TablesEqual(DataTable table1, DataTable table2, params int[] skipColumns)
{
if (table1.Rows.Count != table2.Rows.Count)
return false;
for (int i = 0; i < table1.Rows.Count; i++)
{
var array1 = table1.Rows[i].ItemArray
.Where((obj, index) => !skipColumns.Contains(index));
var array2 = table2.Rows[i].ItemArray
.Where((obj, index) => !skipColumns.Contains(index)); ;
if (!array1.SequenceEqual(array2))
{
return false;
}
}
return true;
}
You just have to pass the indices of the columns you want o ignore, like:
bool allEqual = TablesEqual(t1, t2); // all equal
bool equalIgnore0 = TablesEqual(t1, t2, 0); // ignore first column
bool equalIgnore0and2 = TablesEqual(t1, t2, 0, 2); // ignore first and third column
Finally I am able to solve this issue. The following is my function. I am using the function TablesEqual as provided by Tim Schmelter. The function will return a valid FeeID which is a positive integer else -1 if no match is found.
private int MatchFeeID(DataTable Dt)
{
DataTable mainDt = bl.GetDataSet("Select * From FeeMaster"); //assume this is a function that will return all the fee structures from database.
var fids = (from row in mainDt.AsEnumerable().Distinct()
group row by row.Field<string>("fid") into rowGroup
select new
{
fid = rowGroup.Key
});
foreach (var fid in fids)
{
string id = fid.fid;
DataTable t1 = new DataTable();
DataTable t2 = new DataTable();
DataRow[] dr1 = mainDt.Select(String.Format("fid = '{0}'", id));
t1 = dr1.CopyToDataTable();
t2 = Dt;
bool res = TablesEqual(t1, t2, 0, 1);
if (res) return Convert.ToInt32(id);
}
return -1;
}

linq to datatable incorrect column calculation

Trying to calculate a column value with linq. It sums correctly but produces incorrect value when dividing or multiplying. Example is below.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataTable dtWidgets = Get_widgets();
DataTable dtHits = Get_hits();
var hit_rate =
from w in dtWidgets.AsEnumerable()
join h in dtHits.AsEnumerable()
on w[0] equals h[0]
select new
{
Date = w.Field<DateTime>("calendar_date"),
Widgets = w.Field<int>("widgets"),
Hits = h.Field<int>("hits"),
TestSum = w.Field<int>("widgets")+h.Field<int>("hits"),
TestMult = w.Field<int>("widgets") * h.Field<int>("hits")
};
gvWidgets.DataSource = dtWidgets;
gvWidgets.DataBind();
gvHits.DataSource = dtHits;
gvHits.DataBind();
gvLinq.DataSource = hit_rate.ToArray();
gvLinq.DataBind();
}
static DataTable Get_widgets()
{
DataTable widgets = new DataTable();
widgets.Columns.Add("calendar_date", typeof(DateTime));
widgets.Columns.Add("widgets", typeof(int));
widgets.Rows.Add("05/15/2012", 200000);
widgets.Rows.Add("05/16/2012", 210000);
return widgets;
}
static DataTable Get_hits()
{
DataTable hits = new DataTable();
hits.Columns.Add("calendar_date", typeof(DateTime));
hits.Columns.Add("hits", typeof(int));
hits.Rows.Add("05/15/2012", 100000000);
hits.Rows.Add("05/16/2012", 120000000);
return hits;
}
}
Returns:
Date Widgets Hits TestSum TestMult
5/15/2012 12:00:00 AM 200000 100000000 100200000 -1662697472
5/16/2012 12:00:00 AM 210000 120000000 120210000 1426874368
Given the example numbers you have listed underneath the code, it looks like you're getting integer overflow. Try making the field type Int64.
If I where you, I would look at the limit of int in c#... (2147483647)
Try with long (= Int64)
20000000000000
>
2147483647

Categories