I'm creating Datatables from .csv files. This part actually works. My current issue is the following one:
I have to compare two or more Datatable's with the same structure. So
Datatable1:
KeyColumn, ValueColumn
KeyA, ValueA
KeyB, ValueB
KeyC, ValueC
Datatable2:
KeyColumn, ValueColumn
KeyB, ValueB
KeyC, ValueC
KeyD, ValueD
And this should end up like this:
ResultDatatable:
KeyColumn, ValueColumn (of DT1), ValueColumn (of DT2)
KeyA, ValueA
KeyB, ValueB (of DT1), ValueB (of DT2)
KeyC, ValueC (of DT1), ValueC (of DT2)
KeyD, ValueD
I can't even manage to insert the Data of the first Datatable because of different ColumnNames. Another problem is, that the Datatables own the same ColumnNames, so I can't add those to the ResultDatatable.
I have tried many ways and end up with no solution. Any ideas how to address this problem?
Edit:
The solution with Dictionaries was too sophisticated, so I continued trying to solve it with the Datatables. The source of the problem was something very unexpected.
The attempt to rename a column name to something, which contains a simple dot ('.') results with losing all data in that column.
e.g. If you have Datatable dt:
PrimaryColumn, ValueColumn
KeyA1, KeyB1
KeyA2, KeyB2
After dt.Columns[ValueColumn].ColumnName = "Value.Column"; You will lose any data in that column. I will ask MS, if this is desired or if it is a Bug in the .NET-Framework. Here is my final Code (C#). I have List<string>keys which will remain in the resultTable. and List<string>values which will be added for every Table that should be compared.
private DataTable CompareTables(List<AnalyseFile> files, Query query, List<string> keys, List<string> values) {
// Add first table completely to resultTable
DataTable resultTable =
files[0].GetDataTable(false, query.Header, query.Startstring, query.Endstring, query.Key).Copy();
foreach (string value in values) {
resultTable.Columns[value].ColumnName = "(" + files[0].getFileNameWithoutExtension() + ") " + value;
}
// Set primary keys
resultTable.PrimaryKey = keys.Select(key => resultTable.Columns[key]).ToArray();
// process remaining tables
for (int i = 1; i < files.Count; i++) {
DataTable currentTable = files[i].GetDataTable(false, query.Header, query.Startstring, query.Endstring, query.Key);
// Add value-columns to the resultTable
foreach (string value in values) {
resultTable.Columns.Add("(" + files[i].getFileNameWithoutExtension() + ") " + value);
}
// Set again primary keys
currentTable.PrimaryKey = keys.Select(key => currentTable.Columns[key]).ToArray();
// populate common Rows
foreach (DataRow dataRow in resultTable.Rows) {
foreach (DataRow row in currentTable.Rows) {
foreach (string key in keys) {
if (dataRow[key].ToString().Equals(row[key].ToString())) {
foreach (string value in values) {
string colname = "(" + files[i].getFileNameWithoutExtension() + ") " + value;
dataRow[colname] = row[value];
}
}
}
}
}
// Get all Rows, which do not exist in resultTable yet
IEnumerable<string> isNotinDT =
currentTable.AsEnumerable()
.Select(row => row.Field<string>(keys[0]))
.Except(resultTable.AsEnumerable().Select(row => row.Field<string>(keys[0])));
// Add all the non existing rows to resulTable
foreach (string row in isNotinDT) {
DataRow currentRow = currentTable.Rows.Find(row);
DataRow dRow = resultTable.NewRow();
foreach (string key in keys) {
dRow[key] = currentRow[key];
}
foreach (string value in values) {
dRow["(" + files[i].getFileNameWithoutExtension() + ") " + value] = currentRow[value];
}
resultTable.Rows.Add(dRow);
}
}
return resultTable;
}
Any improvements are Welcome!
Ok Here is an example of my version using the dictionaries.
Fiddle: http://dotnetfiddle.net/AljK9J
//Setup Sample Data
var data1 = new Dictionary<string, string>();
data1.Add("KeyA", "ValueA");
data1.Add("KeyB", "ValueB");
data1.Add("KeyC", "ValueC");
var data2 = new Dictionary<string, string>();
data2.Add("KeyB", "ValueB");
data2.Add("KeyC", "ValueC");
data2.Add("KeyD", "ValueD");
//Second DataType in the Dictionary could be something other than a Tuple
var result = new Dictionary<string, Tuple<string, string>>();
//Fill in for items existing only in data1 and in both data1 and data2
foreach(var item in data1)
{
result.Add(item.Key, new Tuple<string, string>(item.Value, data2.FirstOrDefault(x => x.Key == item.Key).Value));
}
//Fill in remaining items that exist only in data2
foreach(var item in data2.Where(d2 => !result.Any(x => x.Key == d2.Key )))
{
result.Add(item.Key, new Tuple<string, string>(null, item.Value));
}
//Demonstrating how to access the data
var formattedOutput = result.Select(x => string.Format("{0}, {1} (of D1), {2} (of D2)", x.Key, x.Value.Item1 ?? "NoValue", x.Value.Item2 ?? "NoValue"));
foreach(var line in formattedOutput)
{
Console.WriteLine(line);
}
Related
from an API I get a json like this:
66 results of an player with somewhat 31 attributes containing single values or an array of values.
{"api":
{"results":66,
"players":
[{
"player_id":10,
"player_name":"Gustavo Ferrareis",
... (some 31 stats)
"shots":{
"total":13,
"on":2
},
...
},
"player_id":21,
...
}]
}
And I wanted to know if there's a way to deserialize the collection of players into an Dictionary or better DataTable with all 31 attributes without a custom player class or accessing every attribute individually?
So far I tried accessing the players list by:
var data = JObject.Parse(json);
foreach (var field in data)
{
var data2 = JObject.Parse(field.Value.ToString());
foreach (var field2 in data2)
{
if (field2.Key.ToString() == "players")
{
dynamic array2 = JsonConvert.DeserializeObject(field2.Value.ToString());
foreach (var field3 in array2)
Console.WriteLine("Player_id: " + field3.player_id.ToString() + " - Player_name: " + field3.player_name.ToString());
}
}
}
which returns
Player_id: 10 - Player_name: Gustavo Ferrareis
Player_id: 22 - Player_name: GetĂșlio
Player_id: 22 - Player_name: GetĂșlio
I imagine something like:
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (var player in array2)
dict.Add(player.Key(), player.Value());
The answer can't be that I have to make an custom player class and then use that?
Open for any advice.
Thank you.
You can use Newtonsoft.Json.Linq and get the required result as shown below:
var jObject = JObject.Parse(jsonFromAPI)["api"];
var formattedPlayers = jObject["Players"].Children()
.Select(p => $"Player_id: {p["player_id"]} - Player_name: {p["player_name"]}");
or if you wanted dictionary, then use below:
var playersDictionary = jObject["Players"].Children().Select(p => new {player_id = p["player_id"], player_name = p["player_name"]}).ToDictionary(x => x.player_id, v => v.player_name);
If you want to display all properties of Players, then you need to run loop something like below:
var allPlayerDetails = new List<Dictionary<string, object>>();
foreach (JObject player in jObject["Players"].Children())
{
var playerDictionary = player.Properties()
.ToDictionary<JProperty, string, object>(property => property.Name, property => property.Value);
allPlayerDetails.Add(playerDictionary);
}
for (var index = 0; index < allPlayerDetails.Count; index++)
{
var playerDictionary = allPlayerDetails[index];
Console.WriteLine(Environment.NewLine);
Console.WriteLine(string.Format("Printing Player# {0}", index));
foreach (var d in playerDictionary)
{
Console.WriteLine(d.Key + " - " + d.Value);
}
}
If you want to convert to DataTable from list of players, then you can do something like below:
DataTable dt = new DataTable();
foreach (var column in allPlayerDetails.SelectMany(p => p.Keys).Select(k => k.Trim()).Distinct())
{
dt.Columns.Add(new DataColumn(column));
}
foreach (var details in allPlayerDetails)
{
var dr = dt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
dr[dc.ColumnName] = details.ContainsKey(dc.ColumnName) ? details[dc.ColumnName] : null;
}
dt.Rows.Add(dr);
}
Fiddler can be found here.
You could parse into IEnumerable<string> like this:
IEnumerable<string> = JObject.Parse(json)["players"]
.Children()
.Select(jo => $"Player_id: {jo["player_id"]} - Player_name: {jo["player_name"]});
A similar approach would work for Dictionary using ToDictionary instead of Select, but it depends on what you consider key and value.
Here is the single line code to get the playerid and playername to List or Dictionary
//To List
var resultToList = JObject.Parse(jsonstring)["api"]["players"]
.Select(p => (p["player_id"].ToString(), p["player_name"].ToString()))
.ToList();
//To Dictionary
var resultToDict = JObject.Parse(jsonstring)["api"]["players"]
.Select(p => (p["player_id"].ToString(), p["player_name"].ToString()))
.ToDictionary(x=>x.Item1, y=>y.Item2);
I have a file consisting of a list of text which looks as follows:
Example csv file
There csv file has consist of 3 columns. The first columns will always be the length of 5. So I want to loop through the file content, store those first 5 letters as Key and remaining column as value. I am removing comma between them and Substringing as follows to store.
static string line;
static Dictionary<string, string> stations = new Dictionary<string, string>();
static void Main(string[] args)
{
// Dictionary<string, List<KeyValuePair<string, string>>> stations = new Dictionary<string, List<KeyValuePair<string, string>>>();
var lines = File.ReadAllLines(".\\ariba_sr_header_2017122816250.csv");
foreach (var l in lines)
{
line = l.Replace(",", "");
stations.Add(line.Substring(14),line.Substring(14, line.Length-14));
}
//read all key and value in file
foreach (KeyValuePair<string, string> item in stations)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value);
}
Console.ReadLine();
}
After debug, the output is
Output
My Expected Result is as follow:
Expected Result
I cannot see any KeyValuePair here. You have
00021,00014,Ordered
00021,00026,Ordered
00024,00036,Ordered
...
and you want
00021
00021
00024
000014Ordered
000026Ordered
000036Ordered
...
outcome which seems to be IEnumerable<string>. You can try Linq for this
var result = File
.ReadLines(".\\ariba_sr_header_2017122816250.csv")
.Line(line => line.Split(','))
.SelectMany(items => new string[] {
items[0],
$"0{items[1]}{items[2]}" })
.OrderBy(item => item.Length);
foreach (var item in result)
Console.WriteLine(item);
Here we Split each line like 00021,00014,Ordered into separate items: {00021, 00014, Ordered}anf then combine them back with a help ofSelectMany`. We want
00021 which is items[0]
000014Ordered which is 0 + items[1] + items[2]
Finally we want to have short items first - OrderBy(item => item.Length)
Here you go:
var stations = new Dictionary<string, string>();
var lines = File.ReadAllLines(#"C:\temp\22.txt");
foreach (var l in lines)
{
var lsplit = l.Split(',');
if (lsplit.Length > 1)
{
var newkey = lsplit[0];
var newval = lsplit[1] + lsplit[2];
stations[newkey] = newval;
}
}
//read all key and value in file
foreach (KeyValuePair<string, string> item in stations)
{
Console.WriteLine(item.Key + " = " + item.Value);
}
Console.ReadLine();
Not exactly the output you expected, but hopefully it helps.
I am here today trying to work out how I can do this. I have the code below to look through each column in a DataRow, but how can I access the key AND value? I want to assign it to a dictionary in the class but I can't seem to get both of them, the only way I can get anything is by calling:
var columnValue = playerDataRow[column];
Here is the full thing:
using (var mysqlConnection = Sirius.GetServer().GetDatabaseManager().GetConnection())
{
mysqlConnection.SetQuery("SELECT * FROM `users` WHERE `auth_ticket` = #authTicket LIMIT 1");
mysqlConnection.AddParameter("authTicket", authTicket);
var playerDataTable = mysqlConnection.GetTable();
foreach (DataRow playerDataRow in playerDataTable.Rows)
{
foreach (DataColumn column in playerDataTable.Columns)
{
var columnValue = playerDataRow[column];
}
}
}
foreach (DataRow playerDataRow in playerDataTable.Rows)
{
var myDic = new Dictionary<string, object>();
foreach (DataColumn column in playerDataTable.Columns)
{
myDic.Add(column.ColumnName, playerDataRow[column]);
}
}
the variable column will be the key and the value will be columnValue
looks that you only want one row of output - perhaps for this specific user based on auth_ticket
here is an example of how to get all values for this row into a Dictionary of strings (I'm converting all data to strings by the way just for this example)
var htRowValues = new Dictionary<string,string>();
using (var mysqlConnection = Sirius.GetServer().GetDatabaseManager().GetConnection())
{
mysqlConnection.SetQuery("SELECT * FROM `users` WHERE `auth_ticket` = #authTicket LIMIT 1");
mysqlConnection.AddParameter("authTicket", authTicket);
var playerDataTable = mysqlConnection.GetTable();
foreach (DataRow playerDataRow in playerDataTable.Rows)
{
foreach (DataColumn column in playerDataTable.Columns)
{
var columnValue = playerDataRow[column];
htRowValues[column.ColumnName]=System.Convert.ToString(columnValue);
}
}
}
now you have all column values in the dictionary for this one row of data.
I have a DataTable from which I would like to loop through each row and column and then select a value from a specific column depending on the other values in the columns/each row.
My code currently looks like this:
foreach (DataRow drow in dt.Rows)
{
foreach (DataColumn dcol in dt.Columns)
{
foreach (var Item in ImportData)
{
if (Item.Value.Equals(true))
{
if (Item.Key.Equals("" + dcol))
{
string value = drow[dcol].ToString();
if (value.Equals("X"))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
}
}
}
}
ImportData is a Dictionary<string, bool>, which holds the data that I want to compare with my DataTable.
string outDraws is just a string which should hold the content of the drawings I want to print out.
My problem now is that I only want to print out the content in the column 'Drawing' of the row where all columns with the same name as the Keys in ImportData have 'X' as value. At the moment I'm getting all the rows where any of the columns have 'X' as value and has the same name as any Key in ImportData.
I understand that it will be quite hard for you to get what I want to do but please ask if you need any more information and I will try to provide.
Many thanks in advance.
Edit:
ImportData contains the name of different products as keys. These products have either been selected or not by the customer through another program, if they have been selected they have the value true and if not selected they have the value false.
With the method presented above I would like to compare ALL the keys that have the value true with the column names in the DataTable. If the column name corresponds to the key in ImportData (which is the name of a product) then I want to check if that column in a specific row has 'X' as value.
This goes on for ALL the keys in ImportData and in the end I should know which row in the DataTable that has an 'X' in all the columns with the same name as the keys in ImportData. For this row I would like to get the content of the column called 'Drawing'.
So for an example say that ImportData contains:
[Motor, true][Product6, true][Product7, true]
Then I would like to print out the column Drawing at row 6.
Unfortunately I can't post pictures..
As with any problem: divide and conquer. Break down your problem in smaller pieces and go from there.
From what I understand, you want to do something with certain rows from the datatable. Something like:
foreach (var drow in dt.Rows.OfType<DataRow>())
{
if (SomeConditionIsMet(dt, drow, ImportData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
The function SomeConditionIsMetcould looks like this:
private static bool SomeConditionIsMet(
DataTable dt, DataRow drow,
IDictionary<string, bool> importData)
{
// TODO if the condition is met, return true
// otherwise, return false
}
Now your problem is simplified to thinking about what it means that 'Some condition is met'. Once you can clearly express that in words, rename the function to reflect that (e.g. to 'AllColumnsAreChecked')
Here's a sample with solution as I understand it:
internal class Program
{
private static void Main(string[] args)
{
var importData = new Dictionary<string, bool>()
{
{"Product1", true},
{"Product2", false},
{"Product3", true},
};
var dt = new DataTable();
dt.Columns.Add("Product1");
dt.Columns.Add("Product2");
dt.Columns.Add("Product3");
dt.Columns.Add("Product4");
dt.Columns.Add("Drawing");
// row1 should be added
var row1 = dt.NewRow();
row1["Product1"] = "X";
row1["Product3"] = "X";
row1["Drawing"] = "Drawing1";
dt.Rows.Add(row1);
// row2 should not be added
var row2 = dt.NewRow();
row2["Product1"] = "X";
row2["Drawing"] = "Drawing2";
dt.Rows.Add(row2);
string outDraws = string.Empty;
foreach (DataRow drow in dt.Rows.OfType<DataRow>())
{
if (AllColumnsAreChecked(drow, importData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
Console.WriteLine(outDraws);
}
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
foreach (var key in importData.Keys)
{
if (!importData[key])
continue;
var value = drow[key] as string;
if (value != "X")
return false;
}
}
}
Bonus: here's a LINQ based implementation of the check:
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
return importData.Keys
.Where(k => importData.ContainsKey(k) && importData[k]) // the field must be enabled in importData
.All(k => (drow[k] as string) == "X"); // the corresponding value in the row must be 'X'
}
Try this
DataTable tbl = new DataTable();
foreach (DataRow row in tbl.Rows)
{
object cellData = row["colName"];
}
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):