How to add multi let operator and orderby in linq - c#

I have more columns in datatable (about 100 columns). See examble below:
Column 1 | Column 2 | Column 3 | ..... | Column n
1;Nick | 1 | USA | ..... | Value 1
2;David | 2 | Reston | ..... | Value 2
3;Marry | 3 | Spain | ..... | Value 3
4;Join | 4 | Italy 3 | ..... Value 4
Dictionary<string, string > dict = new Dictionary<string, string>();
dict.Add("Column_1", "asc");
dict.Add("Column_2", "asc");
dict.Add("Column_1", "desc");
...................
dict.Add("Column_n", "asc");
var myRows = from row in datatable.AsEnumerable()
let myID = int.Parse(row.Field<string>("Column 2"))
let name = row.Field<string>("Column 1").Split(';')[1]
....
orderby myID ascending, name descending, ......, column n asc
select row;
My problem is how to loop all items in Dictionary to add more let statements and orderby all columns in Dictionary.
Please help me to fix it. My english is not good, sorry

Dictionary<string, string > dict = new Dictionary<string, string>();
dict.Add("col1", "ASC");
dict.Add("col2", "ASC");
dict.Add("col3", "DESC");
DataTable dt = new DataTable();
dt.Columns.Add("col1");
dt.Columns.Add("col2");
dt.Columns.Add("col3");
Random r = new Random();
for (int i = 0; i < 20; i++)
dt.Rows.Add(r.Next(10), r.Next(4), r.Next(2));
string sort = "";
foreach (KeyValuePair<string, string> entry in dict)
sort = sort + " " + entry.Key + " " + entry.Value + ",";
sort = sort.Substring(0, sort.Length - 1).Trim();
dt.DefaultView.Sort = sort;
dt = dt.DefaultView.ToTable();

Related

how to spilt a string with separator into 5 columns [duplicate]

This question already has answers here:
How to get the ever first five character of a string with separator
(4 answers)
Closed 2 years ago.
How can I extract this whole string into 5 columns?
"1;2;3;4;5;A;AAA;AA;AAAA;AAAAA;NA;NA;PASS;YES;NO;test1;test2;test3;test4;test5;Word4;Word5;Word3;Word2;Word1"
Like this:
And I want to put the final result in a List by rows;
eg:
result 1 =
result 2 =
The question does not stipulate a final data structure.
Let's suppose we want something like this at the end:
List<string[]>
{
string[]{ "1", "A", "NA", "test1", "Word4" },
string[]{ "2", "AAA", "NA", "test2", "Word5" },
...
}
A better structured data structure would be more suitable.
And let's suppose that the input string does not contain words having the separator ;
var data = "1;2;3;4;5;A;AAA;AA;AAAA;AAAAA;NA;NA;PASS;YES;NO;test1;test2;test3;test4;test5;Word4;Word5;Word3;Word2;Word1";
var splitData = data.Split(';');
var columnCount = 5;
// We check that the input data has the right number of elements (multiple of columnCount).
if (splitData.Length % columnCount != 0)
{
throw new InvalidOperationException();
}
var results = splitData
.Select((columnVal, i) => (columnVal, i)) // projection of each element including its index.
.GroupBy( x => x.i % columnCount, // group elements by 'row' [0,5,10,15,20][1,6,11,16,21][2,7,12,17,22]...
x => x.columnVal, // selecting only the value (we don't need the index anymore).
(_, columns) => columns.ToArray()) // putting all column values for each row in an array, here we could map to a different data structure.
.ToList();
// the 'results' variable has the target data structure.
If we display the parsed data on the console:
for (var i = 0; i < results.Count; i++)
{
var row = results[i];
// each column value 'i' is in 'result[i]'
var columnStr = string.Join(" | ", row);
Console.WriteLine($"Row {i} -> {columnStr}");
}
/*
Row 0 -> 1 | A | NA | test1 | Word4
Row 1 -> 2 | AAA | NA | test2 | Word5
Row 2 -> 3 | AA | PASS | test3 | Word3
Row 3 -> 4 | AAAA | YES | test4 | Word2
Row 4 -> 5 | AAAAA | NO | test5 | Word1
*/
Here is the solution with assertion tests
string input = "1;2;3;4;5;A;AAA;AA;AAAA;AAAAA;NA;NA;PASS;YES;NO;test1;test2;test3;test4;test5;Word4;Word5;Word3;Word2;Word1";
var elementSplit = input.Split(';');
int columnNumber = 5;
List<string> result = new List<string>();
for (int columnIndex = 0; columnIndex < columnNumber; columnIndex++)
{
StringBuilder row = new StringBuilder();
for (int i = columnIndex; i < elementSplit.Length; i+= columnNumber)
{
row.Append(elementSplit[i]);
row.Append(";");
}
row.Remove(row.Length - 1, 1);
result.Add(row.ToString());
}
Assert.AreEqual("1;A;NA;test1;Word4", result[0]);
Assert.AreEqual("2;AAA;NA;test2;Word5", result[1]);

Merge two datatables in C#?

I have two datatables ..
DataTable dtTemp= new DataTable();
dtTemp.Columns.AddRange(new[]
{
new DataColumn("segment_id", typeof(int)),
new DataColumn("seg_description")
});
DataTable dtTemp2 = new DataTable();
dtTemp2.Columns.Add("set_id",typeof(int));
Now lets have some rows into first table..
segment_id|seg_description
------ |---------------
1 | desc..
2 | desc2..
3 | desc3..
Now lets have some data into second table..
set_id
--------
1
--------
2
Now, I want marge this two tables to get below output
set_id | segment_id |seg_description
--------| ---------- | --------------
1 | 1 | desc..
1 | 2 | desc2..
1 | 3 | desc3..
2 | 1 | desc..
2 | 2 | desc2..
2 | 3 | desc3..
How can I do this?using Merge() can I achieve this?
So you want to "cross-join" the tables by building a cartesian product of all rows? Of course there is no builtin way, you can use this method:
public static DataTable CrossJoinTables(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null)
throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
IEnumerable<object[]> crossJoin =
from r1 in t1.AsEnumerable()
from r2 in t2.AsEnumerable()
select r1.ItemArray.Concat(r2.ItemArray).ToArray();
foreach(object[] allFields in crossJoin)
{
t3.Rows.Add(allFields);
}
return t3;
}
Usage:
DataTable tblresult = CrossJoinTables(dtTemp2, dtTemp); // swapped order because you want columns from dtTemp2 first
To do this you need to use the CROSS JOIN operation.
Select * Table1 CROSS JOIN Table2
It literally gives you the product of the two tables : Every row in A joined to every row in B. if A has 100 rows and B has 100 rows, the Cross Join has 10,000 rows.
How about this:
var dt1 = dtTemp1.AsEnumerable();
var dt2 = dtTemp2.AsEnumerable();
var q = from x in dt1
from y in dt2
select new { set_id = (int)y["set_id"], segment_id = (int)x["segment_id"], seg_description = (string)x["seg_description"] };

How to compare 2 columns in different datatables

table1
id | fileName | fileDateTime
1 | somefile | somedatetime
2 | somefile2 | somedatetime2
table2
id | fileName | fileDateTime
| somefile1 | somedatetime1
| somefile2 | somedatetime2
output table3
id | fileName | fileDatetime
| somefile1 | somedatetime1
I want to compare the 2 tables (column 2 & 3 only) and only have what is not in both tables, there is no ID field in the 2nd table. Then I plan on parsing the data in the file and add file info to database to record file has been parsed. I am having trouble comparing the 2 fields. This does not seem to work.
for (int i = 0; i < finalTable.Rows.Count; i++)
{
for (int r = 0; r < filesTable.Rows.Count; i++)
{
if (finalTable.Rows[i][2] == filesTable.Rows[r][2])
{
finalTable.Rows.Remove(finalTable.Rows[i]);
}
}
}
Assuming the value is a string, you could just do
for (int i = 0; i < finalTable.Rows.Count; i++)
{
for (int r = 0; r < filesTable.Rows.Count; i++)
{
if (finalTable.Rows[r].Field<string>(2) == filesTable.Rows[r].Field<string>(2))
{
finalTable.Rows.Remove(finalTable.Rows[i]);
}
}
}
If it's another type, just change the <string> for the real type !
You can use Linq to achieve it as shown below.
Assuming your fields are string. For other datatype you can cast them accordingly:
using System.Data.Linq;
......
......
// Merge both tables data and group them by comparing columns values
dt1.Merge(dt2);
var g = dt1.AsEnumerable()
.GroupBy(x => x[0]?.ToString() + x[1]?.ToString()) // You can build Unique key here, I used string concatination
.ToDictionary(x => x.Key, y => y.ToList());
var unique = dt1.Clone();
foreach (var e in g)
{
if (e.Value.Count() == 1) // In either table - Intersaction
{
e.Value.CopyToDataTable(unique, LoadOption.OverwriteChanges);
}
if (e.Value.Count() == 2)
{
// In both tables -Union
}
}

Merging two datatable in memory and grouping them to get sum of columns.Using linq but kind of lost here

I have two table where two column are fixed. Some columns are identical and some are new.Columns are dynamic.
Have to do it in code level and I am trying to loop and conditions
What I want is to generate a report following the condition,
All columns in table1 and table2 must be present.
If a column is common and value is there it should be added with the identical row in other table.
If any row is present in one table but not in other, it should be included.
Example data
Table1
ID | NAME | P1 | P2 | P3
----------------------------
1 | A1 | 1 | 2 | 3.3
2 | A2 | 4.4 | 5 | 6
TABLE 2
ID | NAME | P1 | P2 | P4
---------------------------
1 | A1 | 10 | 11 | 12
2 | A2 | 12 | 14 | 15
3 | A3 | 16 | 17 | 18
Expected output:
ID | NAME | P1 | P2 | P3 | P4
---------------------------------
1 | A1 | 11 | 13 | 3.3 | 12
2 | A2 | 16.4 | 19 | 6 | 15
3 | A3 | 16 | 17 | null| 18
Progress till now:
First I merged those two table in to table1
table1.Merge(table2)
Then trying to group by over it
var query = from row in table1.AsEnumerable()
group row by new
{
ID = row.Field<int>("ID"),
Name = row.Field<string>("Name")
}
into grp
select new
{
ID = grp.Key.ID,
Name = grp.Key.Name,
Phase1 = grp.Sum(r => r.Field<decimal>("P1"))
};
I have modified this code to get a datatable. Please see attached cs file.
This is working, but as the number of columns are dynamic, I guess I have to repeat it for other columns and join all these small tables where one columns will be added.
How can I merge all those small tables?
I am lost here.Is there any other way. Its feeling as stupid thing.
Any help would be appreciated.
Attached File:
http://dl.dropbox.com/u/26252340/Program.cs
You want to use an implementation of a full outer join. Something like what follows.
Some setup so you can try this yourself:
DataTable t1 = new DataTable();
t1.Columns.Add("ID", typeof(int));
t1.Columns.Add("Name", typeof(string));
t1.Columns.Add("P1", typeof(double));
t1.Columns.Add("P2", typeof(double));
t1.Columns.Add("P3", typeof(double));
DataRow dr1 = t1.NewRow();
dr1["ID"] = 1;
dr1["Name"] = "A1";
dr1["P1"] = 1;
dr1["P2"] = 2;
dr1["P3"] = 3.3;
t1.Rows.Add(dr1);
DataRow dr2 = t1.NewRow();
dr2["ID"] = 2;
dr2["Name"] = "A2";
dr2["P1"] = 4.4;
dr2["P2"] = 5;
dr2["P3"] = 6;
t1.Rows.Add(dr2);
DataTable t2 = new DataTable();
t2.Columns.Add("ID", typeof(int));
t2.Columns.Add("Name", typeof(string));
t2.Columns.Add("P1", typeof(double));
t2.Columns.Add("P2", typeof(double));
t2.Columns.Add("P4", typeof(double));
DataRow dr3 = t2.NewRow();
dr3["ID"] = 1;
dr3["Name"] = "A1";
dr3["P1"] = 10;
dr3["P2"] = 11;
dr3["P4"] = 12;
t2.Rows.Add(dr3);
DataRow dr4 = t2.NewRow();
dr4["ID"] = 2;
dr4["Name"] = "A2";
dr4["P1"] = 12;
dr4["P2"] = 14;
dr4["P4"] = 15;
t2.Rows.Add(dr4);
DataRow dr5 = t2.NewRow();
dr5["ID"] = 3;
dr5["Name"] = "A3";
dr5["P1"] = 16;
dr5["P2"] = 17;
dr5["P4"] = 18;
t2.Rows.Add(dr5);
The queries look like:
var ids = (from r1 in t1.AsEnumerable() select new { ID = r1["ID"], Name = r1["Name"] }).Union(
from r2 in t2.AsEnumerable() select new { ID = r2["ID"], Name = r2["Name"] });
var query = from id in ids
join r1 in t1.AsEnumerable() on id equals new { ID = r1["ID"], Name = r1["Name"] } into left
from r1 in left.DefaultIfEmpty()
join r2 in t2.AsEnumerable() on id equals new { ID = r2["ID"], Name = r2["Name"] } into right
from r2 in right.DefaultIfEmpty()
select new
{
ID = (r1 == null) ? r2["ID"] : r1["ID"],
Name = (r1 == null) ? r2["Name"] : r1["Name"],
P1 = (r1 == null) ? r2["P1"] : (r2["P1"] == null) ? r1["P1"] : (double)r1["P1"] + (double)r2["P1"],
P2 = (r1 == null) ? r2["P2"] : (r2["P2"] == null) ? r1["P2"] : (double)r1["P2"] + (double)r2["P2"],
P3 = (r1 == null) ? null : r1["P3"],
P4 = (r2 == null) ? null : r2["P4"]
};
Got this solved by
table1.Merge(table2, true, MissingSchemaAction.Add);
finalTable = table1.Clone();
finalTable.PrimaryKey = new DataColumn[] { finalTable.Columns["ID"], finalTable.Columns["Name"] };
List<string> columnNames = new List<string>();
for (int colIndex = 2; colIndex < finalTable.Columns.Count; colIndex++)
{
columnNames.Add(finalTable.Columns[colIndex].ColumnName);
}
foreach (string cols in columnNames)
{
var temTable = new DataTable();
temTable.Columns.Add("ID", typeof(int));
temTable.Columns.Add("Name", typeof(string));
temTable.Columns.Add(cols, typeof(decimal));
(from row in table1.AsEnumerable()
group row by new { ID = row.Field<int>("ID"), Team = row.Field<string>("Team") } into grp
orderby grp.Key.ID
select new
{
ID = grp.Key.ID,
Name = grp.Key.Team,
cols = grp.Sum(r => r.Field<decimal?>(cols)),
})
.Aggregate(temTable, (dt, r) => { dt.Rows.Add(r.ID, r.Team, r.cols); return dt; });
finalTable.Merge(temTable, false, MissingSchemaAction.Ignore);
}
Since the columns are dynamic you'll need to return an object with dynamic properties. You could do this with an ExpandoObject.
The following code is ugly in many ways - I would do some massive refactoring before letting it go - but it gets the job done and might help you out to achieve what you want.
(Sorry for using the other linq syntax.)
var query = table1.AsEnumerable()
.GroupBy(row => new
{
ID = row.Field<int>("ID"),
Name = row.Field<string>("Name")
})
.Select(grp =>
{
dynamic result = new ExpandoObject();
var dict = result as IDictionary<string, object>;
result.ID = grp.Key.ID;
result.Name = grp.Key.Name;
foreach (DataRow row in grp)
{
foreach (DataColumn column in table1.Columns)
{
string columnName = column.ColumnName;
if (columnName.Equals("ID") || columnName.Equals("Name"))
continue;
//else
if (!dict.Keys.Contains(columnName))
dict[columnName] = row[columnName];
else
{
if (row[columnName] is System.DBNull)
continue;
if (dict[columnName] is System.DBNull)
{
dict[columnName] = row[columnName];
continue;
}
//else
dict[columnName] = (decimal)dict[columnName] + (decimal)row[columnName];
}
}
}
return result;
});

Group by two columns in DataTable Column sum

In the following code for finding sum of Rate column in the DataTable dt:
dt.Compute("Sum(Convert(Rate, 'System.Int32'))");
In this is it possible to assign group by clause like SQL Inn order to get Sum based on a value in another column of the same dt Fo eg:
----------------------------
Rate | Group |type
----------------------------
100 | A | 1
120 | B | 2
70 | A | 1
50 | A | 2
----------------------------
I just wanna to get.
Sum A=170(type-1) SUMA=50(type-2) an Sum B=120(type-2)
Ref: I got ans in my previous question in single column case
Group by in DataTable Column sum.
You can group by an anonymous type:
var result = from r in dt.AsEnumerable()
group r by new { Group = r["Group"], Type = r["Type"] } into g
select new { Group = g.Key.Group,
Type = g.Key.Type,
Sum = g.Sum(x => Convert.ToInt32(x["Rate"])) };
foreach (var r in result)
{
Console.WriteLine("Group {0}, Type {1}, Sum {2}", r.Group, r.Type, r.Sum);
}

Categories