adding spec flow table data into hashtable - c#

| FieldName | header | code |
| a1 | test | |
| a1 | test | 1 |
| a1 | test | 2 |
| a1 | test | 3 |
| a1 | test | 4 |
| a1 | test | 5 |
| b2 | prod | 1 |
| b2 | prod | 2 |
| b2 | prod | 3 |
I have the following code to loop thru the table and add the data in a hashtable.
Currently my code only adds one a1 and b2. but what I am planing to do is add them all in my hashtable with a different key.
For example: ("a11",value), ("a12",value); ("a13",value); and the same for b2..ect.
public Hashtable GetData(Table table, string headerType)
{
var data = table.CreateSet<SpecFlowData>();
var hashtable = new Hashtable();
hashtable.Clear();
foreach (var currentRow in data)
{
var key = currentRow.FieldName;
var value = new Hashtable();
GetValue(value, currentRow);
if (hashtable.ContainsKey(key)) //continue;
{
var r = new Random();
key = key + r.Next();
}
var format = (string)value["header"];
if (headerType == format)
{
hashtable.Add(key, value);
}
break;
}
return hashtable;
}
Update: Here is the getvalue method:
private static void GetValue(Hashtable value, SpecFlowData currentRow)
{
value.Clear();
value.Add("code", currentRow.Code);
value.Add("name", currentRow.FieldName);
value.Add("header", currentRow.HeaderType);
}
Why is my data not added properly. thanks for your help.

You are breaking out of the foreach loop at the end of your first iteration, so there can only be one key-value-pair in your Hashtable.
If you remove the break statement, you will get more values. As #JohnGardner mentioned, you should not use a random, because it may produce identical values. Simply use an increasing integer variable.
So after all, this should do:
public Hashtable GetData(Table table, string headerType)
{
var data = table.CreateSet<SpecFlowData>();
var hashtable = new Hashtable(); // no need to clear a newly created Hashtable
int i = 1;
foreach (var currentRow in data)
{
var key = currentRow.FieldName;
var value = GetValue(currentRow);
if (hashtable.ContainsKey(key))
{
key = key + i++;
}
var format = (string)value["header"];
if (headerType == format)
{
hashtable.Add(key, value);
}
}
return hashtable;
}
private static Hashtable GetValue(SpecFlowData currentRow)
{
var value = new Hashtable();
value.Add("code", currentRow.Code);
value.Add("name", currentRow.FieldName);
value.Add("header", currentRow.HeaderType);
}

Related

C# Alternative to Nested Ordered Dictionaries?

I'm having issues with a program I'm developing. The basic essence of the program is to look through a file of election data and organize it via nested objects. For example, each individual Political Race is an object, and each Political Race object has a list of Candidate and County Results objects and so on.
My current issue revolves around the previously mentioned County Results object. I'm supposed to iterate through the file, and record the Candidate's name and the number of votes they got for each county. Currently I am using nested Ordered Dictionaries to achieve this, but it seems clunky and I am having an issue accessing them. Here's my code so far (listOrdicRows is the text file of the election read into an ordered list):
public CountyResults(List<OrderedDictionary> listOrdicRows, String raceCode)
{
foreach (OrderedDictionary row in listOrdicRows)
{
bool duplicate = false;
foreach (County indivCounty in CountyList)
{
if (indivCounty.countyName == row["county_name"].ToString() && raceCode == row["race_code"].ToString())
{
duplicate = true;
break;
}
}
if (!duplicate && raceCode == row["race_code"].ToString())
{
CountyList.Add(new County(row["county_code"].ToString(), row["county_name"].ToString(), row["precincts"].ToString(), row["precincts_reporting"].ToString()));
}
}
populateCountyDict(listOrdicRows);
}
public void populateCountyDict(List<OrderedDictionary> listOrdicRows) //Dynamically populates County Dictionary
{
foreach (County x in CountyList)
{
String CountyName = x.countyName;
List<OrderedDictionary> candidatesWithVotes = null;
foreach (OrderedDictionary row in listOrdicRows)
{
if (CountyName == row["county_name"].ToString())
{
OrderedDictionary tempDictionary = new OrderedDictionary();
tempDictionary.Add(row["candidate_name"], row["total_votes"]);
candidatesWithVotes.Add(tempDictionary);
}
}
countyDict.Add(CountyName, candidatesWithVotes);
}
}
Any help would be appreciated, as I'm exceedingly stuck. Someone asked for what the file looks like, and here's a few lines
ElectionDate | PartyCode | PartyName | RaceCode | OfficeDesc | CountyCode | CountyName | Juris1num | Juris2num | Precincts | PrecinctsReporting | CanNameLast | CanNameFirst | CanNameMiddle | CanVotes
------------ | --------- | ---------- | -------- | ---------------------------- | ---------- | ---------- | --------- | --------- | --------- | ------------------ | ----------- | ------------ | ------------- | --------
2020/08/18 | REP | Republican | USR | United States Representative | ESC | Escambia | 001 | | 0 | 0 | Gaetz | Matt | | 29272
2020/08/18 | REP | Republican | USR | United States Representative | HOL | Holmes | 001 | | 6 | 6 | Gaetz | Matt | | 2131
2020/08/18 | REP | Republican | USR | United States Representative | OKA | Okaloosa | 001 | | 52 | 52 | Gaetz | Matt | | 25861
Linq can make it a bit easy and readable.
I have created a sample file to just few columns
Next is code
public class ElectionInfo
{
public string Race { get; set; }
public string County { get; set; }
public string FName { get; set; }
public int VoteCnt { get; set; }
}
static void Main(string[] args)
{
Dictionary<string, List<ElectionInfo>> dict1 = File.ReadAllLines(#"C:\x1\TextFile2.txt")
.Select(record => record.Split(','))
.Select(cell => new ElectionInfo() { Race = cell[0], County = cell[1], FName = cell[2], VoteCnt = int.Parse(cell[3]) })
.GroupBy(x => x.Race)
.ToDictionary(t => t.Key, t => t.ToList<ElectionInfo>())
;

DataTable.Select() to display summation of records in datatable

In my C# project in a DataTable, I need to sum a few columns and display the aggregated record and I am unable to create filter query for that.
Records like:
|Col1|Col2|Col3|Col4|
| A | X | 10 | 10 |
| A | X | 10 | 20 |
| A | Y | 12 | 12 |
| A | Y | 10 | 10 |
Result will be:
|Col1|Col2|Col3|Col4|
| A | X | 20 | 30 |
| A | Y | 22 | 22 |
I have to use DataTable.Select("filter condition").
var result = (from DataRow s in yourDataTable.Select("filter conditions").AsEnumerable()
group s by new {g1 = s.Field<string>("Col1"), g2 = s.Field<string>("Col2") } into g
select new
{
Col1 = g.Key.g1,
Col2 = g.Key.g2,
Col3 = g.sum(r => r.Field<decimal>("Col3")),
Col4 = g.sum(r => r.Field<decimal>("Col4")),
}).ToList();
And if you want result as DataTable type, you can convert list to DataTable Like below:
var resultAsDataTable = ConvertListToDataTable(result);
public static DataTable ConvertListToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}

Sort the two date columns individually and cumulative effect?

I have two date columns and two size columns (one size column related to one date column) like you can see in the Following table. Now I want the two arrays where in the first array it will be sort by Collected and show the cumulative effect of CollectedSize and same with Staged and StagedSize.
Required:
Collected array |
1/1/2016 | 1
11/1/2016 | 4
12/1/2016 | 6
30/1/2016 | 11
Staged array |
13/1/2016 | 3
14/1/2016 | 7
18/1/2016 | 13
16/1/2016 | 20
Table:
| Collected | CollectedSize | Staged | StagedSize |
| 11/1/2016 | 3 | 14/1/2016 | 4
| 12/1/2016 | 2 | 13/1/2016 | 3
| 30/1/2016 | 5 | 18/1/2016 | 7
| 01/1/2016 | 1 | 16/1/2016 | 6
Currently using the following code:
public class ProductionDataOverTimeVM
{
public ProductionDataOverTimeVM()
{
Collected = new List<TimeChartXAxis>();
Staged = new List<TimeChartXAxis>();
}
public List<TimeChartXAxis> Collected { get; set; }
public List<TimeChartXAxis> Staged { get; set; }
}
public class TimeChartXAxis
{
public string x { get; set; }
public string y { get; set; }
}
var queryResults = context.Datasets.ToList();
ProductionDataOverTimeVM obj = new ProductionDataOverTimeVM();
long? collectedBytes = 0;
long? Staged = 0;
foreach (var dataset in queryResults.OrderBy(d => d.Collected))
{
if (dataset.Collected != null)
{
collectedBytes = collectedBytes + dataset.CollectedSize;
obj.Collected.Add(new TimeChartXAxis
{
x = dataset.Collected != null ? BasicHelpers.FromUTCDate(dataset.Collected, parms.Offset).Value.ToString("dd/M/yyyy") : null,
y = BasicHelpers.FormatBytesToSpecificFormat(collectedBytes, format, false)
});
}
}
foreach (var dataset in queryResults.OrderBy(d => d.Staged))
{
if (dataset.Staged != null)
{
Staged = Staged + dataset.StagedSize;
obj.Staged.Add(new TimeChartXAxis
{
x = dataset.Staged != null ? BasicHelpers.FromUTCDate(dataset.Staged, parms.Offset).Value.ToString("dd/M/yyyy") : null,
y = BasicHelpers.FormatBytesToSpecificFormat(Staged, format, false)
});
}
}
What will be the best approach to do that?
What about
var arrayofOrderByString = new []{"Collected","Staged"}
foreach(var key in arrayofOrderByString){
var y=0;
SortList<Datasets>(queryResults, key, SortDirection.Descending);
queryResults.foreach(s =>{
y=s.GetType().GetProperty(key).GetValue(s, null);
obj.Collected.Add(new ProductionDataOverTimeVM{
x =BasicHelpers.FromUTCDate(s.GetType().GetProperty(key).GetValue(s, null), parms.Offset).Value.ToString("dd/M/yyyy"),
y=collectedBytes
})
})
}
public void SortList<T>(List<T> list, string columnName, SortDirection direction)
{
var property = typeof(T).GetProperty(columnName);
var multiplier = direction == SortDirection.Descending ? -1 : 1;
list.Sort((t1, t2) => {
var col1 = property.GetValue(t1);
var col2 = property.GetValue(t2);
return multiplier * Comparer<object>.Default.Compare(col1, col2);
});
}

WPF Datagrid C#

I know there are a bunch of questions on this in here and tons of information elsewhere. I cannot, for some reason, get this to work. Here is one of my starting points... Add entire row to DataTable at once using list
This is for a List of Lists. The very first List should be the column headers.
dat is a List<List<string>> that looks like:
{"index", "filename0", "filename1"},
{"A-100", "yes", "no"},
{"A-200", "no", "yes"}
etc...
Code:
/// Dictionary containing as Key => FileName
/// as Value => All drawing numbers found in FileName
Dictionary<string, List<string>> AllDrawingLists = new Dictionary<string, List<string>>();
private void processGrid()
{
List<string> index = new List<string>();
/// Build a comprehensive INDEX from the dictionary - A list
/// of all the drawing numbers found in all the FIlenames
foreach (KeyValuePair<string, List<string>> item in AllDrawingLists)
{
foreach (string dwg in item.Value)
{
if (index.Contains(dwg) == false)
{
index.Add(dwg); }
}
}
List<List<string>> dat = new List<List<string>>();
List<String> headers = new List<string>();
headers.Add("Index");
foreach (KeyValuePair<string, List<string>> item in AllDrawingLists)
{
headers.Add(item.Key);
}
dat.Add(headers);
foreach(string i in index)
{
List<string> row = new List<string>();
row.Add(i);
foreach(KeyValuePair<string, List<string>> item in AllDrawingLists)
{
string cell = "no";
if (item.Value.Contains(i))
{
cell = "yes";
}
row.Add(cell);
}
dat.Add(row);
}
dataGrid.Columns.Clear();
DataTable dt = new DataTable();
int ii = 0;
foreach (List<string> row in dat)
{
if (ii == 0)
{
foreach(string t in row)
{
dt.Columns.Add(t);
}
ii++;
} else
{
dt.Rows.Add(row.ToArray<string>());
}
}
dataGrid.ItemsSource = dt.AsDataView();
}
My expected result would be :
| | | |
| index | file1 | file2 |
-------------------------
| A-100 | yes | no |
-------------------------
| A-200 | no | yes |
-------------------------
| A-300 | yes | yes |
but instead I get :
| | | |
| index | file1 | file2 |
-------------------------
| A-100 | | |
-------------------------
| A-200 | | |
-------------------------
| A-300 | | |
The List of Lists is what I would expect, clearly its working for the definition of columns. I'm not sure why nothing goes into the DataGrid after the first column
Here is the output of dat. It is what I think Im looking for. All rows and column accounted for.
Index C:\py\narrver2\lists.txt C:\py\narrver2\list2.docx
A-1001 yes yes
A-1002 yes yes
A-1003 yes yes
A-1004 no yes
A-1005 no yes
A-1006 no yes
A-1007 no yes
In case you want to keep the dot in the header name, you can set the header column binding path with the name of the header surrounded by square brackets to make the special character (in this case, the dot notation) escaped.
You can do that inside an event handler that subscribes to event AutoGeneratingColumn of your DataGrid.
private void dataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName.Contains('.') && e.Column is DataGridBoundColumn)
{
DataGridBoundColumn dataGridBoundColumn = e.Column as DataGridBoundColumn;
dataGridBoundColumn.Binding = new Binding("[" + e.PropertyName + "]");
dataGridBoundColumn.SortMemberPath = e.PropertyName;
}
}

Compare 2 Datatables to find difference/accuracy between the columns

So, I have 2 separate datatables, that look pretty identical but the values in their rows might be different for instance.
EDIT:
I can have an unique ID BY creating a temporary identity column that can be used as primary key if that will make it easier. so think of ID column as the primary key than.
Table A
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 50 | 150 | 35
2 | Bill | 55 | 47 | 98
3 | Pat | 10 | 15 | 45
4 | Cat | 70 | 150 | 35
Table B
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 30 | 34 | 67
2 | Bill | 55 | 47 | 98
3 | Pat | 100 | 15 | 45
4 | Cat | 70 | 100 | 20
Output Should be:
Table C
ID | Name | TableAValue1 | TableBValue1 | DiffValue1 ....Samething for Value2 .....samething for value3
------------------------------------------------------
1 | Bob | 50 | 30 | 20
2 | Bill | 55 | 55 | 0
3 | Pat | 10 | 100 | 90
4 | Cat | 70 | 70 | 0
I Know the tedious method to do this is by using a forloop and looping through each row comparing column rows with each other. But I am not sure how to create a new Table C with the results I want. Also I think there might be a simpler solution using Linq which I am not very familiar with but I would be interested in the solution with linq if it faster and less lines of code. I am looking for the most optimal/efficient way of going about this. as these datatables can be anywhere between 5,000 to 15,000+ rows in size so memory usage becomes an issue.
LINQ is not faster, at least not in general. But it can help to increase readability.
You can use Enumerable.Join which might be more efficient than nested loops, but you need a loop to fill your third table anyway. So the first two columns are the identifiers and the rest are the values:
var query = from r1 in table1.AsEnumerable()
join r2 in table2.AsEnumerable()
on new { ID = r1.Field<int>("ID"), Name = r1.Field<string>("Name") }
equals new { ID = r2.Field<int>("ID"), Name = r2.Field<string>("Name") }
select new { r1, r2 };
var columnsToCompare = table1.Columns.Cast<DataColumn>().Skip(2);
foreach (var rowInfo in query)
{
var row = table3.Rows.Add();
row.SetField("ID", rowInfo.r1.Field<int>("ID"));
row.SetField("Name", rowInfo.r1.Field<int>("Name"));
foreach (DataColumn col in columnsToCompare)
{
int val1 = rowInfo.r1.Field<int>(col.ColumnName);
int val2 = rowInfo.r2.Field<int>(col.ColumnName);
int diff = (int)Math.Abs(val1-val2);
row.SetField(col.ColumnName, diff);
}
}
var tableC = new DataTable();
tableC.Columns.Add(new DataColumn("ID"));
tableC.Columns.Add(new DataColumn("Name"));
tableC.Columns.Add(new DataColumn("TableAValue1"));
tableC.Columns.Add(new DataColumn("TableBValue1"));
tableC.Columns.Add(new DataColumn("DiffValue1"));
foreach (DataRow rowA in tableA.Rows)
{
foreach (DataRow rowB in tableB.Rows)
{
if (Convert.ToInt32(rowA["ID"]) == Convert.ToInt32(rowB["ID"]) &&
rowA["Name"].ToString() == rowB["Name"].ToString() &&
Convert.ToInt32(rowA["Value1"]) != Convert.ToInt32(rowB["Value1"]))
{
var newRow = tableC.NewRow();
newRow["ID"] = rowA["ID"];
newRow["Name"] = rowA["Name"];
newRow["TableAValue1"] = rowA["Value1"];
newRow["TableBValue1"] = rowB["Value1"];
newRow["DiffValue1"] = Convert.ToInt32(rowA["Value1"]) - Convert.ToInt32(rowB["Value1"]);
tableC.Rows.Add(newRow);
}
}
}
Using LINQ, create an anonymous type as follows
var joinedRows = (from rowA in TableA.AsEnumerable()
from rowB in TableB.AsEnumerable()
where rowA.Field<String>("Name") == rowB.Field<String>("Name")
select new
{
ID = rowA.Field<int>("ID"),
Name = rowA.Field<String>("Name"),
TableAValue1 = rowA.Field<int>("Value1"),
TableBValue1 = rowB.Field<int>("Value1"),
DiffValue1 = Math.Abs(rowA.Field<int>("Value1") - rowB.Field<int>("Value1")),
TableAValue2 = rowA.Field<int>("Value2"),
TableBValue2 = rowB.Field<int>("Value2"),
DiffValue2 = Math.Abs(rowA.Field<int>("Value2") - rowB.Field<int>("Value2")),
TableAValue3 = rowA.Field<int>("Value3"),
TableBValue3 = rowB.Field<int>("Value3"),
DiffValue3 = Math.Abs(rowA.Field<int>("Value3") - rowB.Field<int>("Value3"))
});
Table.AsEnumerable() will give you an IEnumerable(of DataRow)
row.Field will cast it to the correct type for you
You can now use the anonymous type of joinedRows and create your new dataTable from it
This uses a strategy similar to kippermand's, but will probably perform slightly better on large sets of data by avoiding the O(n²) complexity of checking every ID against every other ID, and by reusing the values extracted from the data table:
// joining by row location
var joinedTableRows =
dt1.AsEnumerable().Zip(dt2.AsEnumerable(),
(r1, r2) => new{r1, r2});
// or, joining by ID
var joinedTableRows2 =
dt1.AsEnumerable().Join(dt2.AsEnumerable(),
r => r.Field<int>("ID"),
r => r.Field<int>("ID"),
(r1, r2) => new{r1, r2});
var result =
from row in joinedTableRows
let rowA = row.r1
let rowB = row.r2
let tableAValue1 = rowA.Field<int>("Value1")
let tableBValue1 = rowB.Field<int>("Value1")
let tableAValue2 = rowA.Field<int>("Value2")
let tableBValue2 = rowB.Field<int>("Value2")
let tableAValue3 = rowA.Field<int>("Value3")
let tableBValue3 = rowB.Field<int>("Value3")
select new
{
ID = row.r1.Field<int>("ID"),
Name = row.r1.Field<string>("Name"),
TableAValue1 = tableAValue1,
TableBValue1 = tableBValue1,
DiffValue1 = Math.Abs(tableAValue1 - tableBValue1),
TableAValue2 = tableAValue2,
TableBValue2 = tableBValue2,
DiffValue2 = Math.Abs(tableAValue2 - tableBValue2),
TableAValue3 = tableAValue3,
TableBValue3 = tableBValue3,
DiffValue3 = Math.Abs(tableAValue3 - tableBValue3)
};
Depending on how your data needs to be consumed, you could either declare a class matching this anonymous type, and consume that directly (which is what I'd prefer), or you can create a DataTable from these objects, if you have to.

Categories

Resources