I have a DataTable that I would like to convert into dictionary in C# for my project. I can use the traditional way of programming to achieve the goal but it is not as elegant as using linq/lambda. I tried to use Lambda but I got stuck in how to flatten multiple rows into 1.
I have a mock DataTable for testing purpose.
static DataTable GetData()
{
DataTable table = new DataTable();
table.Columns.Add("Field1", typeof(string));
table.Columns.Add("Field2", typeof(string));
table.Rows.Add("A", "A1");
table.Rows.Add("A", "A2");
table.Rows.Add("B", "B1");
table.Rows.Add("A", "A3");
table.Rows.Add("C", "C1");
table.Rows.Add("D", "D1");
table.Rows.Add("A", "A5");
return table;
}
My traditional way to convert it to Dictionary is:
Dictionary<string, ArrayList> t = new Dictionary<string, ArrayList>();
foreach (DataRow r in GetData().Rows)
{
string k = (string)r["Field1"];
string v = (string)r["Field2"];
if (!t.Keys.Contains(r["Field1"]))
{
t.Add(k, new ArrayList());
}
if (t.Values == null)
{
t[k] = new ArrayList();
}
t[k].Add(v);
}
How do I achieve the same thing with Linq?
I have tried:
var res = GetData()
.AsEnumerable()
.GroupBy(row => row.Field<string>("Field1"))
.Select(grp => grp.First());
This only gives me the first occurrence of the item. I am stuck.
Please help.
Actually, you don't want to convert it to a Dictionary, but to a Lookup. Here's an example:
var lookup = GetData().AsEnumerable()
.ToLookup(r => r.Field<string>("Field1"), r => r.Field<string>("Field2"));
foreach (var grouping in lookup)
{
Console.WriteLine(grouping.Key + ": " + String.Join(", ", grouping));
}
Output:
A: A1, A2, A3, A5
B: B1
C: C1
D: D1
Get Data from Datatable as Dictionary without Linq/Lambda
DataTable dataTable = GetData();
var data = new List<Dictionary<string, object>>();
foreach (DataRow dataTableRow in dataTable.Rows)
{
var dic = new Dictionary<string, object>();
foreach (DataColumn tableColumn in dataTable.Columns)
{
dic.Add(tableColumn.ColumnName, dataTableRow[tableColumn]);
}
data.Add(dic);
}
you can get a Collection:
var res = GetData()
.AsEnumerable()
.Select(grp => new KeyValuePair<string, string>(grp[0].ToString(), grp[1].ToString()));
Related
I want to get all DataColumns (of double type) of my DataTable in Lists and then create a Dictionary where the Key would be the header of the DataColumn and the Value would be the List with the data of the DataColumn. How can I achieve this with LINQ?
I tried the following lines without success:
// Create Dictionary
Dictionary<string, List<double>> DataDic = new Dictionary<string, List<double>>();
// Create List
List<double> DataList = new List<double>();
// For each DataColumn save it as a List of double
DataList = (from DataColumn dc in dt.Columns select new double()).ToList();
// Add KVP to DataDic
DataDic.Add(column.ColumnName, DataList);
Thanks in advance.
That is pretty straight forward:
// Create Dictionary
var DataDic = dt.Columns.Cast<DataColumn>()
.Where(dc => dc.DataType == typeof(double))
.ToDictionary(dc => dc.ColumnName,
dc => dt.AsEnumerable()
.Select(r => r.Field<double>(dc.ColumnName))
.ToList()
);
I have a datatable in which no of columns is dynamic. I need a list with header name and value.I found a question similar to this question.
enter link description here
But can not getting desired output.
Output with this solution
In Dynamic View :
In Result View
I m using this Code for Convert to dynamic object :
public static List<dynamic> ToDynamic(this DataTable dt)
{
var dynamicDt = new List<dynamic>();
foreach (DataRow row in dt.Rows)
{
dynamic dyn = new ExpandoObject();
foreach (DataColumn column in dt.Columns)
{
var dic = (IDictionary<string, object>)dyn;
dic[column.ColumnName] = row[column];
}
dynamicDt.Add(dyn);
}
return dynamicDt;
}
Need a output like that :-
How can i achieve this?
You can achieve that by modifying the ToDynamic method you are using. The original method is:
public static class DataTableExtensions
{
public static List<dynamic> ToDynamic(this DataTable dt)
{
var dynamicDt = new List<dynamic>();
foreach (DataRow row in dt.Rows)
{
dynamic dyn = new ExpandoObject();
dynamicDt.Add(dyn);
//--------- change from here
foreach (DataColumn column in dt.Columns)
{
var dic = (IDictionary<string, object>)dyn;
dic[column.ColumnName] = row[column];
}
//--------- change up to here
}
return dynamicDt;
}
}
Replace the lines between the "change" comments into:
foreach (var columnName in new[] {"A", "B", "C", "D", "E"} )
{
var dic = (IDictionary<string, object>)dyn;
if(dt.Columns.Contains(columnName))
dic[columnName] = row[dt.Columns[columnName]];
else
dic[columnName] = 0;
}
That's assuming you need column names A to E, adjust as appropriate if you need more columns.
Currently we are doing this by looping through each value of list and dictionary:
private DataTable ChangeToDictionary(List<Dictionary<string,int>> list)
{
DataTable datatTableReturn = new DataTable();
if (list.Count() > 0)
{
Dictionary<string, int> haeders = list.ElementAt(0);
foreach (var colHead in haeders)
{
datatTableReturn.Columns.Add(colHead.Key);
}
}
foreach (var row in list)
{
DataRow dataRow = datatTableReturn.NewRow();
foreach (var col in row)
{
dataRow[col.Key] = col.Value;
}
datatTableReturn.Rows.Add(dataRow);
}
return datatTableReturn;
}
But is there a better way? Looping through so many times doesn't feel good
The answers above don't address the issue of the dictionary having more than 1 row. This solution addresses the issue.
static DataTable ToDataTable(List<Dictionary<string, int>> list)
{
DataTable result = new DataTable();
if (list.Count == 0)
return result;
var columnNames = list.SelectMany(dict=>dict.Keys).Distinct();
result.Columns.AddRange(columnNames.Select(c=>new DataColumn(c)).ToArray());
foreach (Dictionary<string,int> item in list)
{
var row = result.NewRow();
foreach (var key in item.Keys)
{
row[key] = item[key];
}
result.Rows.Add(row);
}
return result;
}
static void Main(string[] args)
{
List<Dictionary<string, int>> it = new List<Dictionary<string, int>>();
Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("a", 1);
dict.Add("b", 2);
dict.Add("c", 3);
it.Add(dict);
dict = new Dictionary<string, int>();
dict.Add("bob", 34);
dict.Add("tom", 37);
it.Add(dict);
dict = new Dictionary<string, int>();
dict.Add("Yip Yip", 8);
dict.Add("Yap Yap", 9);
it.Add(dict);
DataTable table = ToDictionary(it);
foreach (DataColumn col in table.Columns)
Console.Write("{0}\t", col.ColumnName);
Console.WriteLine();
foreach (DataRow row in table.Rows)
{
foreach (DataColumn column in table.Columns)
Console.Write("{0}\t", row[column].ToString());
Console.WriteLine();
}
Console.ReadLine();
}
And the output looks like...
a b c bob tom Yip Yip Yap Yap
1 2 3
34 37
8 9
Speed, elegance and reusability don't go together. You always choose more important one, and try to balance other two.
Faster the code, uglier it is.
Prettier it is, less reusable it is.
Here's an example of "elegant" solution, but that goes with it not being very readable.
private static DataTable ToDictionary(List<Dictionary<string, int>> list)
{
DataTable result = new DataTable();
if (list.Count == 0)
return result;
result.Columns.AddRange(
list.First().Select(r => new DataColumn(r.Key)).ToArray()
);
list.ForEach(r => result.Rows.Add(r.Select(c => c.Value).Cast<object>().ToArray()));
return result;
}
Try this
List<Dictionary<string, object>> ListDic;
var stringListDic = JsonConvert.SerializeObject(ListDic);
var dataTable = JsonConvert.DeserializeObject<DataTable>(stringListDic);
Try this:
private DataTable GetDataTableFromDictionaries<T>(List<Dictionary<string, T>> list)
{
DataTable dataTable = new DataTable();
if (list == null || !list.Any()) return dataTable;
foreach (var column in list.First().Select(c => new DataColumn(c.Key, typeof(T))))
{
dataTable.Columns.Add(column);
}
foreach (var row in list.Select(
r =>
{
var dataRow = dataTable.NewRow();
r.ToList().ForEach(c => dataRow.SetField(c.Key, c.Value));
return dataRow;
}))
{
dataTable.Rows.Add(row);
}
return dataTable;
}
How about something like the code below?
Good, because it iterates each row exactly once. It should be pretty quick, I've included obvious exceptions to make the code safer.
private static DataTable DictionariesToDataTable<T>(
IEnumerable<IDictionary<string, T>> source)
{
if (source == null)
{
return null;
}
var result = new DataTable();
using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
{
return result;
}
if (e.Current.Keys.Length == 0)
{
throw new InvalidOperationException();
}
var length = e.Current.Keys.Length;
result.Columns.AddRange(
e.Current.Keys.Select(k => new DataColumn(k, typeof(T))).ToArray());
do
{
if (e.Current.Values.Length != length)
{
throw new InvalidOperationException();
}
result.Rows.Add(e.Current.Values);
}
while (e.MoveNext());
return result;
}
}
try my solution, seems very clean to me:
private DataTable DictonarysToDataTable(List<Dictionary<string, int>> list)
{
DataTable table = new DataTable();
foreach (Dictionary<string,string> dict in list) //for every dictonary in the list ..
{
foreach (KeyValuePair<string,int> entry in dict) //for every entry in every dict
{
if (!myTable.Columns.Contains(entry.Key.ToString()))//if it doesn't exist yet
{
myTable.Columns.Add(entry.Key); //add all it's keys as columns to the table
}
}
table.Rows.Add(dict.Values.ToArray()); //add the the Values of every dict in the list as a new row
}
return table;
}
Edit:
Oh Snap, this works only for one Dictionary .. i didn't think it through.
But maybie you can modify it to work for a List of Dictionarys ..
Give this a try please
DataTable table = new DataTable();
foreach (IDictionary<string, object> row in DeviceTypeReport)
{
foreach (KeyValuePair<string, object> entry in row)
{
if (!table.Columns.Contains(entry.Key.ToString()))
{
table.Columns.Add(entry.Key);
}
}
table.Rows.Add(row.Values.ToArray());
}
private DataTable toDataTable(List<RetirementDiskModelDto> retirementDiskModelDtos)
{
DataTable result = new DataTable();
foreach (var col in retirementDiskModelDtos.FirstOrDefault().Items)
result.Columns.Add(col.Key);
foreach (var row in retirementDiskModelDtos)
{
DataRow newrow = result.NewRow();
foreach (var col in retirementDiskModelDtos.FirstOrDefault().Items)
newrow[col.Key] = col.Value;
result.Rows.Add(newrow);
}
return result;
}
Please look at what is wrong? I want to convert datarow to a string list.
public List<string> GetEmailList()
{
// get DataTable dt from somewhere.
List<DataRow> drlist = dt.AsEnumerable().ToList();
List<string> sEmail = new List<string>();
foreach (object str in drlist)
{
sEmail.Add(str.ToString()); // exception
}
return sEmail; // Ultimately to get a string list.
}
Thanks for help.
There's several problems here, but the biggest one is that you're trying to turn an entire row into a string, when really you should be trying to turn just a single cell into a string. You need to reference the first column of that DataRow, which you can do with brackets (like an array).
Try something like this instead:
public List<string> GetEmailList()
{
// get DataTable dt from somewhere.
List<string> sEmail = new List<string>();
foreach (DataRow row in dt.Rows)
{
sEmail.Add(row[0].ToString());
}
return sEmail; // Ultimately to get a string list.
}
Here's a one liner I got from ReSharper on how to do this, not sure of the performance implications, just thought I'd share it.
List<string> companyProxies = Enumerable.Select(
vendors.Tables[0].AsEnumerable(), vendor => vendor["CompanyName"].ToString()).ToList();
Here is how it would be with all additional syntax noise stripped: one-liner.
public List<string> GetEmailList()
{
return dt.AsEnumerable().Select(r => r[0].ToString()).ToList();
}
The Linq way ...
private static void Main(string[] args)
{
var dt = getdt();
var output = dt
.Rows
.Cast<DataRow>()
.ToList();
// or a CSV line
var csv = dt
.Rows
.Cast<DataRow>()
.Aggregate(new StringBuilder(), (sb, dr) => sb.AppendFormat(",{0}", dr[0]))
.Remove(0, 1);
Console.WriteLine(csv);
Console.ReadLine();
}
private static DataTable getdt()
{
var dc = new DataColumn("column1");
var dt = new DataTable("table1");
dt.Columns.Add(dc);
Enumerable.Range(0, 10)
.AsParallel()
.Select(i => string.Format("row {0}", i))
.ToList()
.ForEach(s =>
{
var dr = dt.NewRow();
dr[dc] = s;
dt.Rows.Add(dr);
});
return dt;
}
I need to loop through a List of Dictionaries
List<Dictionary<string,string>>
to populate a DataTable. Each Dictionary in the list has a Key, which needs to be the column name, and a Value which is what's in that column. The list contains 225 dictionaries (225 rows to the table).
List<Dictionary<string, string>> myList =
JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(jsonRep);
DataTable dt = new DataTable();
//loop through list, loop through dictionaries, add keys as columns,
//values as rows.
so far, I have been trying..
//get max columns
int columns = myList[0].Count; <--gives me 13
//add columns
for (int i = 0; i < columns; i++)
dt.Columns.Add(string myList[i].Keys); <--somehow get to the key in dict to add as column names
//add rows
foreach (var x in myList)
{
dt.Rows.Add(x); <--not working
}
jsonReprValue = dt; <--save new DataTable to var jsonReprValue
How do I do this correctly?
Thanks!
You have two problems. One is adding the columns, and the other is adding the rows.
Adding the columns
Assuming your list has items in it, and that all the dictionaries in the list have the same keys, then you only need to add columns from one of the dictionaries:
foreach(string column in myList[0].Keys)
{
dt.Columns.Add(column);
}
Adding the Rows
Change this:
foreach (var x in myList)
{
dt.Rows.Add(x); <--not working
}
To this:
foreach(Dictionary<string, string> dictionary in myList)
{
DataRow dataRow = dt.NewRow();
foreach(string column in dictionary.Keys)
{
dataRow[column] = dictionary[column];
}
dt.Rows.Add(dataRow);
}
See DataTable.NewRow.
Reiview DataTable Class's example on how to work with a data table. But here is an example using Linq
List<Dictionary<string, string>> myList = new List<Dictionary<string, string>>()
{ new Dictionary<string,string>() { { "ABC", "This" },
{ "DEF", "is" },
{ "GHI", "radio" },
{ "JKL", "clash" } } };
DataTable dt = new DataTable();
// Add columns first
dt.Columns.AddRange( myList.First ()
.Select (kvp => new DataColumn() { ColumnName = kvp.Key, DataType = System.Type.GetType("System.String")} )
.AsEnumerable()
.ToArray()
);
// Now add the rows
myList.SelectMany (Dict => Dict.Select (kvp => new {
Row = dt.NewRow(),
Kvp = kvp
}))
.ToList()
.ForEach( rowItem => {
rowItem.Row[rowItem.Kvp.Key] = rowItem.Kvp.Value;
dt.Rows.Add( rowItem.Row );
}
);
dt.Dump();
The result (Dump is LinqPad specific not .Net):