How to count and sum total of DataTable with LINQ? - c#

I have a DataTable which has a column "amount" for each rows and I'd like to have the total sum of all the rows. And also, I'd like to get total number of rows in the DataTable. Could anyone teach me how to have it done with LINQ instead of ordinary way?

Number of rows:
DataTable dt; // ... populate DataTable
var count = dt.Rows.Count;
Sum of the "amount" column:
DataTable dt; // ... populate DataTable
var sum = dt.AsEnumerable().Sum(dr => dr.Field<int>("amount"));

Aggregate allows you to avoid enumerating the rows twice (you could get the row count from the rows collection but this is more to show how to extract multiple aggregates in 1 pass):
var sumAndCount = table.AsEnumerable().Aggregate(new { Sum = 0d, Count = 0},
(data, row) => new { Sum = data.Sum + row.Field<double>("amount"), Count = data.Count + 1});
double sum = sumAndCount.Sum;
int count = sumAndCount.Count;

decimal[] Amount = {2,3,5 };
var sum = Amount.Sum();
var count = Amount.Count();

Based on Roy Goode's Answer you could also create an Extension
public static int Sum(this DataTable table, string Column)
{
return table.AsEnumerable().Sum(dr => dr.Field<int>(Column));
}
Unfortunately you can't be more generic her because there is no where T : numeric

Related

ASP.NET DropDown and SQL C#

I have a number in a database (e.g 12) and I want to display that number in the dropdown list on a webform, however, not the number itself but a range of numbers from 1 to 12 (so 1,2,3....12). Is there a property I can use or a way to get a list of numbers from SQL Statement?
Read the Number from Database
Display a full range of numbers from 1 to X (X = Number from Database)
Bind dropdown list to:
Enumerable.Range(1, <number from database>);
First create an array or list and then iterate till the number you read from database then add these numbers to your array or list then bind with your dropdown datasource
var numbers = new List<int>();
for(var i = DB_NUMBER; i >= 1; i--)
{
numbers.Add(i);
}
yourDropDown.DataSource = numbers;
yourDropDown.DataBind();
Hope it helps
Since, you wish to return the range of numbers from the SQL Query you might need a complex query, but it will always have some limit to it.
SQL Query as per your requirement:
Select Value from
(
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n as Value
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) as tbl
Where value between 0 and 12
ORDER BY 1
I Modified above SQL from the Original Source : Answer
C#:
DropDownList.DataSource = DataTable;
DropDownList.DisplayField = Value;
DropDownList.ValueField = Value;
DropDownList.DataBind();
You just read the number from the database and then use a for loop:
for(int i = readNumber; i > 0; i--)
{
//Add i to your dropDown list or do anything you want with it
}
Based on the question
If I assume that you want SQL to generate and return the range then you want to use a recursive cte to build it from the value in your table ...
// construct the db connection and command object
var con = new SqlConnection("Your Connection String");
using(var cmd = new SqlCommand(con) { CommandType = CommandType.Text })
{
// tell the command what SQL query we want to execute
cmd.CommandText = #"
DECLARE #startnum INT=1
DECLARE #endnum INT= SELECT TOP 1 Number FROM ValueTable
;
WITH gen AS (
SELECT #startnum AS num
UNION ALL
SELECT num+1 FROM gen WHERE num+1<=#endnum
)
SELECT * FROM gen
option (maxrecursion 100)
";
// connect to the db and execute the command
con.Open();
using(var reader = cmd.ExecuteReader())
{
// build the range from the values generated by it
var range = new List<int>();
while(reader.Read()) { range.Add(reader.Read()); }
// bind the results to the drop down on the page
DropDownList.DataSource = range
.Select(i = > new { Key = i, Value = i })
.ToArray();
DropDownList.DisplayField = "Key";
DropDownList.ValueField = "Value";
DropDownList.DataBind();
}
con.Close();
}
The simplest approach
ok querying a db is a pretty well documented problem so I won't repeat that here.
But lets assume you have the following ...
// sourced from your db
int start = 1;
int end = 12;
... from there you can build a range of values ...
var range = Enumerable.Range(start, end)
.Select(i = > new { Key = i, Value = i })
.ToArray();
... and then bind that range to your drop down on the page ...
DropDownList.DataSource = range;
DropDownList.DisplayField = "Key";
DropDownList.ValueField = "Value";
DropDownList.DataBind();
Sources of information ...
How to generate a range of numbers between two numbers?
https://msdn.microsoft.com/en-us/library/fksx3b4f.aspx
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx
To achieve this you need to
Take the desire number from database
then you have to write a for loop.
private void InitializeDropDownList(int number)
{
for (int i = 0; i < number; i++)
{
ddlNumberRange.Items.Add(new ListItem { Text = (i + 1).ToString(), Value = (i + 1).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));

Count the number of occurrences of a string in a row

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!

Re-arrange rows of Datatable in runtime

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();
}

How to create Total column and Total row using LINQ?

Given a datatable that does not have a total column or row. How can I use LINQ to add a Total Column and Total row to create the table below?
TextColumn, NumberColumn0, NumberColumn1 Total
A 1 4 5
B 2 55 47
C 2.3 DBNULL 2.3
D 4 3 7
Total 9.3 62 61.3
Thanks!
Um, for those who need code. I'm using this currently for total column:
public static void CreateTotalColumnByType(DataTable table, Type valueType)
{
// create expression
string expression = string.Empty;
// do not count the first column (used for string field)
for (int columnIndex = 1; columnIndex < table.Columns.Count; ++columnIndex)
{
if (columnIndex != table.Columns.Count - 1)
{
// add +
expression += String.Format("[{0}] + ", table.Columns[columnIndex].ColumnName);
}
else
{
// last column so don't add plus
expression += String.Format("[{0}]", table.Columns[columnIndex].ColumnName);
}
}
// add total column
DataColumn totalColumn = new DataColumn("Total", valueType, expression);
table.Columns.Add(totalColumn);
}
but I'd like to replace it with LINQ.
//adds Total column
var q1 = src.Select(x=>new { x.TextColumn
,x.NumberColumn0
,x.NumberColumn1
,Total=x.NumberColumn0+x.NumberColumn1});
//adds Totals row
var q2 = q1.Concat(new[]{new{ TextColumn="Total"
,NumberColumn0 = src.Sum(_=>_.NumberColumn0)
,NumberColumn1 = src.Sum(_=>_.NumberColumn1)
,Total=q1.Sum(_=>_.Total)}});
There are a number of ways of doing this, depending on how you are getting this back you wouldn't even need to use LINQ.
You could do the following if you are using standard classes for data transport.
public class MyDataClass
{
public string TextColumn {get; set;}
public int NumberColumn0 {get; set;}
public int NumberColumn1 {get; set;}
public int Total
{
get { return NumberColumn0 + NumberColumn1; }
}
}
This way you have no need for LINQ. If you REALLY want to use LINQ you could use
var results = from x in MyDataList
select new { TextColumn = x.TextColumn, NumberColumn0 = x.NumberColumn0,
NumberColumn1 = x.NumberColumn1,
Total = x.NumberColumn1 + x.NumberColumn0};
If you have a variable number of columns or something similar, you might not have as much luck using LINQ, we would need to see more to get a true idea of what you are doing.
You could try like this one.
For each col As DataGridViewColumn in grid.Columns
'' You could try adding if here if there are exception columns
gRID.Item(col.Index, 0).Value = gRID.Rows.Cast(Of DataGridViewRow)().AsEnumerable().Sum(Function(c) Convert.ToDecimal(c.Cells(col.Index).Value)).ToString()
Next
I just enhance p.cambell's solution a bit

Categories