C# DataTable to Dictionary with multiple values - c#

I have a DataTable that returns me data as follows:
APP | VERSION
____|________
app1| 1.0
app1| 2.0
app1| 3.0
app2| 1.0
app3|
As you see, "app1" has multiple versions, and some apps don't have any versions. I need to store this Datatable result in some sort of List for later usage in my code, so I came up with Dictionary, but can't get It to work. Here is what I tried:
public static Dictionary<string, List<string>> AppVer()
{
string SQL = "SELECT column1, column2 FROM myTable a LEFT JOIN
myTable2 v ON v.ID_FK=a.ID_PK";
Dictionary<string, List<string>> versions = new Dictionary<string,
List<string>>();
DataTable dt = new DataTable();
try
{
using (var conn = new OracleConnection(conn_string))
{
conn.Open();
using (OracleCommand cmd = new OracleCommand(SQL, conn))
{
using (OracleDataAdapter dad = new OracleDataAdapter(cmd))
{
dad.Fill(dt);
}
versions = dt.AsEnumerable().ToDictionary<DataRow, string, List<string>>(row => row[0].ToString(), new List<string>(row=> row[1].ToString()));
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return versions;
}
However, I get error
"Cannot convert lambda expression to type 'string' because it is not a
delegate type".
How could I store my DataTable to dictionary ?
EDIT:
Dictionary<string, List<Version>> all_versions = AppVer();
StringBuilder sb = new StringBuilder();
foreach (var item in all_versions)
{
sb.AppendFormat("{0} - {1}{2}", item.Key, item.Value, Environment.NewLine);
}
string result = sb.ToString().TrimEnd();
MessageBox.Show(result);

ILookup<string, string> myLookup = dt.AsEnumerable().ToLookup(row => row[0].ToString(), row => row[1].ToString());
IEnumerable<string> myValues = myLookup["app1"];
// Loop through lookup.
foreach(var group in myLookup)
{
string key = group.Key;
IEnumerable<string> myValues = group;
Console.WriteLine("Group {0} has {1} elements", key, myValues);
}

You need to GroupBy by the key, then you can use ToList on the groups. You know that there's a Version-class which you could use? So i'd prefer a Dictionary<string, List<Version>>:
public static Dictionary<string, List<Version>> AppVer()
{
Dictionary<string, List<Version>> versions = null;
string SQL = "SELECT column1, column2 FROM myTable a LEFT JOIN myTable2 v ON v.ID_FK = a.ID_PK";
var dt = new DataTable();
try
{
using (var conn = new OracleConnection(conn_string))
using (var da = new OracleDataAdapter(SQL, conn))
{
da.Fill(dt);
}
versions = dt.AsEnumerable()
.GroupBy(r => r.Field<string>(0))
.ToDictionary(
g => g.Key,
g => g.Select(r => Version.Parse(r.Field<string>(1))).ToList());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return versions;
}
One of the benefits of the Version class it that it sorts correctly, so 10.1 after 2.1.

Related

Converting Excel to JSON using c# dictionary

I am using this code to convert an Excel file into a JSON format.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var pathToExcel = #"D:\works\sample.xlsx";
var sheetName = "sa";
var connectionString = String.Format(#"
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source={0};
Extended Properties=""Excel 12.0 Xml;HDR=YES""
", pathToExcel);
using (var conn = new OleDbConnection(connectionString))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = String.Format(
#"SELECT * FROM [{0}$]",
sheetName
);
using (var rdr = cmd.ExecuteReader())
{
var query =
(from DbDataRecord row in rdr
select row).Select(x =>
{
Dictionary<string, object> item = new
Dictionary<string, object>();
for(int i=0;i<20;i++)
{
item.Add(rdr.GetName(i), x[i]);
}
return item;
});
//Generates JSON from the LINQ query
var json = JsonConvert.SerializeObject(query);
var result = json;
Console.WriteLine(result);
//return json;
}
}
}
}
}
The requirement is that I need to create sub serialized object within the JSON. For example, if a cell has a value:
"name": "abc,def", the output must be:
"name":
{
"name1": "abc",
"name2": "def"
}
How can I modify my code to achieve this?
try this code this will help you i hope
//dynamic item = new ExpandoObject();
Dictionary<string, object> item = new Dictionary<string, object>();
for(int i=0;i<20;i++)
{
if(x[i].ToString().Contains(","))
{
Dictionary<string, object> temp = new Dictionary<string, object>()
string[] data = x[i].ToString().Split(',');
for(int j=0;j<data.Length;j++)
{
temp.Add(rdr.GetName(i)+j, data[j]);
}
item.Add(rdr.GetName(i), temp);
}
else
{
item.Add(rdr.GetName(i), x[i]);
Console.WriteLine("\n");
}
}

Create an Array with Keys and Values from database

I need to do this in my codes;
Get data from 2 columns in a database (laborer and trx_date)
Place the extracted data to a DataTable
Explode the first column in the DataTable
Place exploded_laborer and trx_date to an array with
key->exploded_laborer
exploded=>trx_date
I am able to get to number 3 I just need to do number 4. My code is below:
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var exploded = new List<string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString().Split('|')[0]);
}
}
Your help is very much appreciated.
I think you are supposed to create Dictionary<TKey,TValue> which represents a collection of keys and values. This might do the trick for you
laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
Thus the complete code might look like
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var LabDict = laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
}
Edit
This is just to create a dummy table.
static DataTable GetTable()
{
// Here we create a DataTable with four columns.
DataTable table = new DataTable();
table.Columns.Add("laborer", typeof(string));
table.Columns.Add("trx_date", typeof(string));
// Here we add five DataRows.
table.Rows.Add("Indocin", "12/12/2010");
table.Rows.Add("Enebrel", "12/1/2011");
table.Rows.Add("Hydralazine", "1/12/2012");
table.Rows.Add("Combivent", "11/12/2013");
table.Rows.Add("Dilantin", "12/11/2014");
return table;
}
normal core C# way
DataTable laborerDataTable = GetTable();
Dictionary<string, string> exploded = new Dictionary<string, string>();
foreach(DataRow row in laborerDataTable.Rows)
{
exploded.Add(row.Field<string>(0), row.Field<string>(1));
}
Also, how do I print each of the row to a console?
foreach(var dct in exploded)
{
Console.WriteLine(dct.Key + " Date is " + dct.Value);
}
If you are expecting the result to a Key-Value pair then why an array? why not a Dictionary<string, string>? You can try something like this :
Dictionary<string, string> laborerDict = laborerDataTable.AsEnumerable()
.ToDictionary(x => x.Field<string>("laborer"),
x => x.Field<string>("trx_date"));
Use in built HashMap in java refer the code below
HashMap map = new HashMap<>();
Map.put(key,value);
And to get data from map use
Map.get(key)
It has a lot of function, maybe you can refer them by searching Java API
when you want to have Key=>Value better you use Dictionary instead of List
var exploded = new Dictionary<string,string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString(),(row["trx_date"].ToString());
}

return raw sql as dictionary

Hello everyone I'm trying to write code that can get select table information with (string)tableName, but i get error when i try to put value to Dictionary.
P.S : I have generated EF DB model.
public Dictionary<string, List<object>> GetTableInformation(string tableName, FinkonaDatabaseType type)
{
Dictionary<string, List<object>> _returnableDictionary = new Dictionary<string, List<object>>();
PropertyInfo prop = optimumEntities.GetType().GetProperty(tableName);
Type tableType = prop.PropertyType.GenericTypeArguments[0];
var items = optimumEntities.Database.SqlQuery(tableType, "SELECT * FROM " + tableName);
foreach (var item in items)
{
foreach (PropertyInfo info in item.GetType().GetProperties())
{
if (!_returnableDictionary.ContainsKey(info.Name))
{
_returnableDictionary.Add(info.Name, new List<object>());
}
_returnableDictionary[info.Name].Add(info.GetValue(info, null));
// System.Reflection.TargetException, Object does not match target type.
}
}
return _returnableDictionary;
}
Using ADO.NET DataTables will be easier here, as EF is used for strongly typing data. Since you are not too worried about the data types coming back, a DataTable will be easier to navigate.
Here is an examples of this:
public Dictionary<string, List<object>> GetTableInformation(string tableName, FinkonaDatabaseType type)
{
var sqlText = "SELECT * from " + tableName;
DataTable dt = new DataTable();
// Use DataTables to extract the whole table in one hit
using(SqlDataAdapter da = new SqlDataAdapter(sqlText, optimumEntities.Database.ConnectionString)
{
da.Fill(dt);
}
var tableData = new Dictionary<string, List<object>>();
// Go through all columns, retrieving their names and populating the rows
foreach(DataColumn dc in dt.Columns)
{
string columnName = dc.Name;
rowData = new List<object>();
tableData.Add(columnName, rowData);
foreach(DataRow dr in dt.Rows)
{
rowData.Add(dr[columnName]);
}
}
return tableData;
}

How to find particular value arraylist

I have one array list:
public ArrayList GetExpenseTypes()
{
ArrayList expArry = new ArrayList();
using (SqlConnection conn = new SqlConnection(connStr))
{
string sql = "SELECT ID, TITLE";
sql += " FROM EXPENSE_TYPE";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader reader;
try
{
conn.Open();
reader = cmd.ExecuteReader();
while (reader.Read())
{
expArry.Add(new ListItem(reader["TITLE"].ToString(), reader["ID"].ToString()));
}
reader.Close();
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
}
return expArry;
}
My ArrayList like this
1 name1
2 name2
3 name3
4 name4
5 name6
if my value is 4 i need to display name4
How i achieve that?
Instead of ArrayList you might want to use Dictionary<string,string> like this.
public IDictionary<string,string> GetExpenseTypes()
{
Dictionary<string,string> expArry = new Dictionary<string,string>();
// Your sql code
while (reader.Read())
{
expArry.Add(reader["TITLE"].ToString(), reader["ID"].ToString());
}
// The rest of your code
}
Then you can get the value like this
var values = GetExpenseTypes();
string valueYouWant = values["4"];
If on the other hand your problem is that when you use the ListItems in a web control you are seeing the wrong values, then you need to swap the parameters when you create the ListItem because the first parameter is the text that is displayed and the second is the value. Like this.
expArry.Add(new ListItem(reader["ID"].ToString(), reader["TITLE"].ToString()));
In which case you should consider using a List<ListItem> instead of ArrayList
you could use BinarySearch method if you are searching for value types; in your case this does not seem possible.
I think you may need to use a loop assuming that you can not use Linq (because of the framework employed);
int index = -1;
for(int i=0; i<expArray.Count;i++)
{
ListItem temp = (expArray[i] as ListItem);
if(temp != null && temp.Value == "some value")
{
index = i;
break;
}
}
I'd recommend first changing ArrayList to List<ListItem>. If you can't do that for some reason you could use (assuming each item is unique):
var x = expArry.Cast<ListItem>().SingleOrDefault(exp => exp.Value == value);
if (x != null)
//item was found
If you know the item will always be there just use Single if it will only be there once.
Don't use ArrayList, do something like this using a generic dictionary
public IDictionary<int, string> GetExpenseTypes()
{
var result = new Dictionary<int, string>();
using (var conn = new SqlConnection(connStr))
{
var getExpenses = new SqlCommand(
"SELECT ID, TITLE FROM EXPENSE_TYPE",
conn);
conn.Open();
using (var reader = command.ExecureReader())
{
while (reader.Read())
{
result.Add(reader.GetInt32(0), reader.GetString(1));
}
}
}
return result;
}
Then you could look up your string like this
var type4 = GetExpenseTypes()[4];
alternatively, don't get the whole list to find one value.
public string GetExpenseType(int id)
{
using (var conn = new SqlConnection(connStr))
{
var getExpenseType = new SqlCommand(
"SELECT TITLE FROM EXPENSE_TYPE WHERE ID = #p0",
conn);
getExpenseType.Parameters.Add(id);
conn.Open();
return (string)getExpenseType.ExecuteScalar();
}
}
If, for some bad reason, you want to stick with your ArrayList returning function, you can find your item like this.
var expenseTypes = GetExpenseTypes().OfType<ListItem>().ToDictionary(
li => int.Parse(li.Text),
li => li.Value);
var type4 = expenseTypes[4];
or, if you want to do this once.
var type4 = GetExpenseTypes().OfType<ListItem>()
.Single(li => li.Text == "4").Value;

Can you get the column names from a SqlDataReader?

After connecting to the database, can I get the name of all the columns that were returned in my SqlDataReader?
var reader = cmd.ExecuteReader();
var columns = new List<string>();
for(int i=0;i<reader.FieldCount;i++)
{
columns.Add(reader.GetName(i));
}
or
var columns = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).ToList();
There is a GetName function on the SqlDataReader which accepts the column index and returns the name of the column.
Conversely, there is a GetOrdinal which takes in a column name and returns the column index.
You can get the column names from a DataReader.
Here is the important part:
for (int col = 0; col < SqlReader.FieldCount; col++)
{
Console.Write(SqlReader.GetName(col).ToString()); // Gets the column name
Console.Write(SqlReader.GetFieldType(col).ToString()); // Gets the column type
Console.Write(SqlReader.GetDataTypeName(col).ToString()); // Gets the column database type
}
Already mentioned. Just a LINQ answer:
var columns = reader.GetSchemaTable().Rows
.Cast<DataRow>()
.Select(r => (string)r["ColumnName"])
.ToList();
//Or
var columns = Enumerable.Range(0, reader.FieldCount)
.Select(reader.GetName)
.ToList();
The second one is cleaner and much faster. Even if you cache GetSchemaTable in the first approach, the querying is going to be very slow.
If you want the column names only, you can do:
List<string> columns = new List<string>();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly))
{
DataTable dt = reader.GetSchemaTable();
foreach (DataRow row in dt.Rows)
{
columns.Add(row.Field<String>("ColumnName"));
}
}
But if you only need one row, I like my AdoHelper addition. This addition is great if you have a single line query and you don't want to deal with data table in you code. It's returning a case insensitive dictionary of column names and values.
public static Dictionary<string, string> ExecuteCaseInsensitiveDictionary(string query, string connectionString, Dictionary<string, string> queryParams = null)
{
Dictionary<string, string> CaseInsensitiveDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
// Add the parameters for the SelectCommand.
if (queryParams != null)
foreach (var param in queryParams)
cmd.Parameters.AddWithValue(param.Key, param.Value);
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable dt = new DataTable();
dt.Load(reader);
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
CaseInsensitiveDictionary.Add(column.ColumnName, row[column].ToString());
}
}
}
}
conn.Close();
}
}
catch (Exception ex)
{
throw ex;
}
return CaseInsensitiveDictionary;
}
Use an extension method:
public static List<string> ColumnList(this IDataReader dataReader)
{
var columns = new List<string>();
for (int i = 0; i < dataReader.FieldCount; i++)
{
columns.Add(dataReader.GetName(i));
}
return columns;
}
For me, I would write an extension method like this:
public static string[] GetFieldNames(this SqlDataReader reader)
{
return Enumerable.Range(0, reader.FieldCount).Select(x => reader.GetName(x)).ToArray();
}
I use the GetSchemaTable method, which is exposed via the IDataReader interface.
You sure can.
protected void GetColumNames_DataReader()
{
System.Data.SqlClient.SqlConnection SqlCon = new System.Data.SqlClient.SqlConnection("server=localhost;database=northwind;trusted_connection=true");
System.Data.SqlClient.SqlCommand SqlCmd = new System.Data.SqlClient.SqlCommand("SELECT * FROM Products", SqlCon);
SqlCon.Open();
System.Data.SqlClient.SqlDataReader SqlReader = SqlCmd.ExecuteReader();
System.Int32 _columncount = SqlReader.FieldCount;
System.Web.HttpContext.Current.Response.Write("SqlDataReader Columns");
System.Web.HttpContext.Current.Response.Write(" ");
for ( System.Int32 iCol = 0; iCol < _columncount; iCol ++ )
{
System.Web.HttpContext.Current.Response.Write("Column " + iCol.ToString() + ": ");
System.Web.HttpContext.Current.Response.Write(SqlReader.GetName( iCol ).ToString());
System.Web.HttpContext.Current.Response.Write(" ");
}
}
This is originally from: http://www.dotnetjunkies.ddj.com/Article/B82A22D1-8437-4C7A-B6AA-C6C9BE9DB8A6.dcik
It is easier to achieve it in SQL
var columnsList = dbContext.Database.SqlQuery<string>("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'SCHEMA_OF_YOUE_TABLE' AND TABLE_NAME = 'YOUR_TABLE_NAME'").ToList();

Categories