Multiple lines and columns in Specflow - c#

I have a Specflow table that looks like this.
When I Perform POST Operation for "/example/" with body
| answerValue1 | answerValue2 | countryCode | Cash |
| Yes | Yes | AD | 76-100% |
| | | AF | |
The column CountryCode is the only one that can be multiple choices.
What I tried to do was to add the columns to dictionary with a simple tableExtenstions
public class TableExtensions
{
public static Dictionary<string, string> ToDictionary(Table table)
{
var dictionary = new Dictionary<string, string>();
foreach (var row in table.Rows)
{
dictionary.Add(row[0], row[1]);
}
return dictionary;
}
}
and call it from the method.
var dictionary = TableExtensions.ToDictionary(table);
var countryCode = dictionary["countryCode"];
Unfortnally I get error The given key was not present in the dictionary,
since the dictionary only returns two values from the first and the second Key
Ofcourse if I change the keys to row[2], row[3] it gets the right columns.
But I would like to reuse the Table Extension.
Also i tried to increment them, but it only took the first to columns
var i = 0;
foreach (var row in table.Rows)
{
dictionary.Add(row[i], row[i]);
i++;
}
Does anyone has a better solution?

I'm not entirely sure what you want the dictionary to ultimately contain, but as you mention that manually changing the rows it looks for to:
row[2], row[3]
gives the data you want, perhaps this would give you the reusability you're looking for:
public class TableExtensions
{
public static Dictionary<string, string> ToDictionary(Table table, int columnOne, int columnTwo)
{
int i = 0;
var dictionary = new Dictionary<string, string>();
foreach (var row in table.Rows)
{
dictionary.Add(row[columnOne], row[columnTwo]);
}
return dictionary;
}
}
Usage:
var dictionary = TableExtensions.ToDictionary(table, 2, 3);
This produces a dictionary with the following contents:
You could get the country code like this:
foreach (var row in dictionary)
{
var countryCode = row.Key;
var score = row.Value ?? string.empty;
}

Given the simplicity of the country codes I would make them a comma separated list and use a vertical table instead:
When I Perform POST Operation for "/example/"
| Field | Value |
| answerValue1 | ... |
| answerValue2 | ... |
| countryCodes | AD, AF |
| cash | ... |
In your step definition:
var example = table.CreateInstance<ExampleRow>();
// use example.GetCountryCodes();
And the ExampleRow class to parse the table into an object:
public class ExampleRow
{
public string AnswerValue1 { get; set; }
public string AnswerValue2 { get; set; }
private string[] countryCodes;
public string CountryCodes
{
get => string.Join(", ", countryCodes);
set => countryCodes = value.Split(", ");
}
public string[] GetCountryCodes()
{
return countryCodes;
}
}

Related

How to map dictionary with values containg tuples to class instance

I have a dictionary of type Dictionary<string, (int Id, string Code, string Name)>:
var dict = new Dictionary<string, (int Id, string Code, string Name)>
{
["subjectType"] = (1, "1Code", "1Name"),
["residentType"] = (2, "2Code", "2Name"),
["businessType"] = (3, "3Code", "3Name"),
// and so on...
};
I use tuple (int Id, string Code, string Name) here but I can replace it with a class. So, it doesn't matter tuple or class, I just need to have three properties (Id, Code, Name) for each dictionary item.
I need to project this dictionary into a class, so I map each property of output model separately like this:
public static OutputModel Map(
Dictionary<string, (int Id, string Code, string Name)> dictionary) =>
new OutputModel
{
SubjectTypeId = dictionary["subjectType"].Id,
SubjectTypeCode = dictionary["subjectType"].Code,
SubjectTypeName = dictionary["subjectType"].Name,
ResidentTypeId = dictionary["residentType"].Id,
ResidentTypeCode = dictionary["residentType"].Code,
ResidentTypeName = dictionary["residentType"].Name,
BusinessTypeId = dictionary["businessType"].Id,
BusinessTypeCode = dictionary["businessType"].Code,
BusinessTypeName = dictionary["businessType"].Name,
// and so on...
};
I wonder is there any other (more fancy) way to do the same mapping?
You could do the following.
var outPutModel = new OutputModel();
foreach (var keyValuePair in dictionary)
outPutModel.Write(keyValuePair);
public class OutputModel
{
public void Write(KeyValuePair<string, (int Id, string Code, string Name)> keyValuePair)
{
var type = typeof(OutputModel);
type.GetProperty(keyValuePair.Key + "Id", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public).SetValue(this, keyValuePair.Value.Id);
type.GetProperty(keyValuePair.Key + "Code", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public).SetValue(this, keyValuePair.Value.Code);
type.GetProperty(keyValuePair.Key + "Name", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public).SetValue(this, keyValuePair.Value.Name);
}
}
See it in action:
https://dotnetfiddle.net/jfKYsG

How to Compare multi dimentional string array?

I want to compare 2 fields. i.e Machine and Pass.
I want to concatenate 'Color' Depending upon the value of Machine and pass.
--------------|------------------|------------
Color1 | Machine 1 | Pass 1
--------------|------------------|------------
Color2 | Machine 2 | Pass 1
--------------|------------------|------------
Color3 | Machine 1 | Pass 1
--------------|------------------|------------
Color4 | Machine 1 | Pass 2
--------------|------------------|------------
Color5 | Machine 2 | Pass 1
--------------|------------------|------------
Color6 | Machine 2 | Pass 2
--------------|------------------|------------
I want results as follows.
Color1/Color3 for Machine1 & Pass1
Color2/Color5 for Machine2 & Pass2
Color4 for Machine1 & Pass2
Color6 for Machine2 & Pass2
I have no clue how to get this results. tried many ways but not getting it the way I want it
First, what you really have here, conceptually, is a single dimension of a complex object that has both a color, machine, and pass value. Storing that in a 2 dimensional array isn't a good idea. A better representation is a List of some custom type, so let's do that conversion first.
First, we'll define a custom type to represent the input data:
public class MachinePass//TODO consider renaming
{
public string Color { get; set; }
public string Machine { get; set; }
public string Pass { get; set; }
}
then we'll parse our input and put it into the appropriate structure:
var list = new List<MachinePass>();
for (int i = 0; i < data.GetLength(0); i++)
{
var next = new MachinePass();
next.Color = data[i, 0];
next.Machine = data[i, 1];
next.Pass = data[i, 2];
list.Add(next);
}
Now that we have this, we can apply our business logic. In this case what you're wanting to do is group these rows by both machine and pass, and then get the colors for those groups. The GroupBy LINQ operator makes this super easy:
var query = list.GroupBy(row => new { row.Machine, row.Pass }
, row => row.Color);
Then we can just print out the results of this query in the defined format:
foreach(var group in query)
Console.WriteLine("{0} for {1} & {2}",
string.Join("/", group),
group.Key.Machine,
group.Key.Pass);
This is your answer,
private void button1_Click(object sender, EventArgs e)
{
var ary = new[]
{
"Color1 | Machine 1 | Pass 1 ",
"Color2 | Machine 2 | Pass 1 ",
"Color3 | Machine 1 | Pass 1 ",
"Color4 | Machine 1 | Pass 2 ",
"Color5 | Machine 2 | Pass 1 ",
"Color6 | Machine 2 | Pass 2 "
};
var seprated = from x in ary.Select(x => x.Split('|'))
select new
{
key = x[1].Trim() + "&" + x[2].Trim(),
value = x[0]
};
var sb = new StringBuilder();
foreach (var key in seprated.Select(x => x.key).Distinct())
{
var colors = seprated.Where(x => x.key == key).Select(x => x.value.Trim()).ToArray();
sb.AppendLine(string.Format("{0} for {1}", string.Join("/", colors), key));
}
textBox1.Text = sb.ToString();
}
sb.toString() has result:
Color1/Color3 for Machine 1&Pass 1
Color2/Color5 for Machine 2&Pass 1
Color4 for Machine 1&Pass 2
Color6 for Machine 2&Pass 2
You did not provide a class definition so I am going to assume it is the following
class PaintMachineInfo
{
public string ColorName {get; set;}
public string MachineName {get; set;}
public string Pass {get; set;}
}
You can easily get the results you want by using .ToLookup(, what it will allow you to do is to provide a key and it will give you a IEnumerable of results that match that key.
I like to use a custom class for the lookup key instead of a Tuple because it makes it much more obvious what you are looking at and it is not too much code to create the class.
private static void Main(string[] args)
{
List<PaintMachineInfo> info = GenerateInfo();
var filteredResults = info.ToLookup(line => new PaintMachineLookup(line.MachineName, line.Pass), line => line.ColorName);
//Contains a IEnumerable<string> containing the elements "Color1" and "Color3"
var result1 = filteredResults[new PaintMachineLookup("Machine 1", "Pass 1")];
}
private static List<PaintMachineInfo> GenerateInfo()
{
//...
}
class PaintMachineInfo
{
public string ColorName { get; set; }
public string MachineName { get; set; }
public string Pass { get; set; }
}
internal class PaintMachineLookup
{
public PaintMachineLookup(string machineName, string pass)
{
MachineName = machineName;
Pass = pass;
}
public string MachineName { get; private set; }
public string Pass { get; private set; }
public override int GetHashCode()
{
unchecked
{
int x = 27;
x = x * 11 + MachineName.GetHashCode();
x = x * 11 + Pass.GetHashCode();
return x;
}
}
public override bool Equals(object obj)
{
var other = obj as PaintMachineLookup;
if (other == null)
return false;
return MachineName.Equals(other.MachineName) && Pass.Equals(other.Pass);
}
}
var multidimensionalArray = new[,]
{
{"Color1", "Machine 1", "Pass 1"},
{"Color2", "Machine 2", "Pass 1"},
{"Color3", "Machine 1", "Pass 1"},
{"Color4", "Machine 1", "Pass 2"},
{"Color5", "Machine 2", "Pass 1"},
{"Color6", "Machine 2", "Pass 2"}
};
var tuple = new List<Tuple<string, string, string>>();
for (var i = 0; i < multidimensionalArray.Length/3-1; i++)
{
tuple.Add(new Tuple<string, string, string>(multidimensionalArray[i, 0], multidimensionalArray[i, 1], multidimensionalArray[i, 2]));
}
foreach (var el in tuple.GroupBy(x => String.Format("{0} & {1}", x.Item2, x.Item3), y => y.Item1))
{
Console.WriteLine(String.Join("/", el) + " for " + el.Key);
}

reading a CSV into a Datatable without knowing the structure

I am trying to read a CSV into a datatable.
The CSV maybe have hundreds of columns and only up to 20 rows.
It will look something like this:
+----------+-----------------+-------------+---------+---+
| email1 | email2 | email3 | email4 | … |
+----------+-----------------+-------------+---------+---+
| ccemail1 | anotherccemail1 | 3rdccemail1 | ccemail | |
| ccemail2 | anotherccemail2 | 3rdccemail2 | | |
| ccemail3 | anotherccemail3 | | | |
| ccemail4 | anotherccemail4 | | | |
| ccemail5 | | | | |
| ccemail6 | | | | |
| ccemail7 | | | | |
| … | | | | |
+----------+-----------------+-------------+---------+---+
i am trying to use genericparser for this; however, i believe that it requires you to know the column names.
string strID, strName, strStatus;
using (GenericParser parser = new GenericParser())
{
parser.SetDataSource("MyData.txt");
parser.ColumnDelimiter = "\t".ToCharArray();
parser.FirstRowHasHeader = true;
parser.SkipStartingDataRows = 10;
parser.MaxBufferSize = 4096;
parser.MaxRows = 500;
parser.TextQualifier = '\"';
while (parser.Read())
{
strID = parser["ID"]; //as you can see this requires you to know the column names
strName = parser["Name"];
strStatus = parser["Status"];
// Your code here ...
}
}
is there a way to read this file into a datatable without know the column names?
It's so simple!
var adapter = new GenericParsing.GenericParserAdapter(filepath);
DataTable dt = adapter.GetDataTable();
This will automatically do everything for you.
I looked at the source code, and you can access the data by column index too, like this
var firstColumn = parser[0]
Replace the 0 with the column number.
The number of colums can be found using
parser.ColumnCount
I'm not familiar with that GenericParser, i would suggest to use tools like TextFieldParser, FileHelpers or this CSV-Reader.
But this simple manual approach should work also:
IEnumerable<String> lines = File.ReadAllLines(filePath);
String header = lines.First();
var headers = header.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
DataTable tbl = new DataTable();
for (int i = 0; i < headers.Length; i++)
{
tbl.Columns.Add(headers[i]);
}
var data = lines.Skip(1);
foreach(var line in data)
{
var fields = line.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
DataRow newRow = tbl.Rows.Add();
newRow.ItemArray = fields;
}
i used generic parser to do it.
On the first run through the loop i get the columns names and then reference them to add them to a list
In my case i have pivoted the data but here is a code sample if it helps someone
bool firstRow = true;
List<string> columnNames = new List<string>();
List<Tuple<string, string, string>> results = new List<Tuple<string, string, string>>();
while (parser.Read())
{
if (firstRow)
{
for (int i = 0; i < parser.ColumnCount; i++)
{
if (parser.GetColumnName(i).Contains("FY"))
{
columnNames.Add(parser.GetColumnName(i));
Console.Log("Column found: {0}", parser.GetColumnName(i));
}
}
firstRow = false;
}
foreach (var col in columnNames)
{
double actualCost = 0;
bool hasValueParsed = Double.TryParse(parser[col], out actualCost);
csvData.Add(new ProjectCost
{
ProjectItem = parser["ProjectItem"],
ActualCosts = actualCost,
ColumnName = col
});
}
}

Generate Sitemap from URLs in Database

Problem Statement:
URLs are stored in a database, example:
home/page1
gallery/image1
info/IT/contact
home/page2
home/page3
gallery/image2
info/IT/map
and so on.
I would like to arrange the above urls into a tree fashion as shown below (each item will be a url link). The final output would be a simple HTML List (plus any sub list(s))
thus:
home gallery info
page1 image1 IT
page2 image2 contact
page3 map
Programming Language is C# , platform is asp.net
EDIT 1:
In the above example, we end up with Three Lists because in our example there is three main 'groups' eg: home, gallery, info.
Naturally, this can change, the algorithm needs to be able to somehow build the lists recursively..
Well,sorting those strings need a lot of work,I've done something similar to your condition.I wish to share the strategy with you.
First of all,(if you can change design of your tables indeed)
Create a table URL like below
----------------
| URL Table |
----------------
| ID |
| ParentID |
| Page |
|..extra info..|
----------------
It's an implementation of category and sub category in same table.In a similar manner,you can contain insert a lot of page and subpage.For example,
-------------------------------------
| ID | ParentID | Page | ...
------------------------------------
| 0 | null | Home |
| 1 | null | Gallery |
| 2 | null | Info |
| 3 | 0 | Page1 |
| 4 | 0 | Page2 |
| 5 | 0 | Page3 | ...
| 6 | 1 | Image1 |
| 7 | 1 | Image2 |
| 8 | 2 | IT |
| 9 | 8 | contact |
| 1 | 8 | map |
------------------------------------- ...
when ParentID is null then its highest level
when ParentID is and ID then its a sublevel of whatever level is on that ID and so on...
From C# side,you know the top pages where ParentID's are null.
You can bring sub pages of them by selected ID's of top pages.It's some ADO.NET work.
Hope this helps
Myra
ok, did it:
First created a class:
public class Node
{
private string _Parent = string.Empty;
private string _Child = string.Empty;
private bool _IsRoot = false;
public string Parent
{
set { _Parent = value; }
get { return _Parent; }
}
public string Child
{
set { _Child = value; }
get { return _Child; }
}
public Node(string PChild, string PParent)
{
_Parent = PParent;
_Child = PChild;
}
public bool IsRoot
{
set { _IsRoot = value; }
get { return _IsRoot; }
}
}
then generated the SiteMap, by transforming the urls strings directly as follows:
private static string MakeTree()
{
List<Node> __myTree = new List<Node>();
List<string> urlRecords = new List<string>();
urlRecords.Add("home/image1");
urlRecords.Add("home/image2");
urlRecords.Add("IT/contact/map");
urlRecords.Add("IT/contact/address");
urlRecords.Add("IT/jobs");
__myTree = ExtractNode(urlRecords);
List<string> __roots = new List<string>();
foreach(Node itm in __myTree)
{
if (itm.IsRoot)
{
__roots.Add(itm.Child.ToString());
}
}
string __trees = string.Empty;
foreach (string roots in __roots)
{
__trees += GetChildren(roots, __myTree) + "<hr/>";
}
return __trees;
}
private static string GetChildren(string PRoot, List<Node> PList)
{
string __res = string.Empty;
int __Idx = 0;
foreach (Node x in PList)
{
if (x.Parent.Equals(PRoot))
{
__Idx += 1;
}
}
if (__Idx > 0)
{
string RootHeader = string.Empty;
foreach (Node x in PList)
{
if (x.IsRoot & PRoot == x.Child)
{
RootHeader = x.Child;
}
}
__res += RootHeader+ "<ul>\n";
foreach (Node itm in PList)
{
if (itm.Parent.Equals(PRoot))
{
__res += string.Format("<ul><li>{0}{1}</li></ul>\n", itm.Child, GetChildren(itm.Child, PList));
}
}
__res += "</ul>\n";
return __res;
}
return string.Empty;
}
private static List<Node> ExtractNode(List<string> Urls)
{
List<Node> __NodeList = new List<Node>();
foreach (string itm in Urls)
{
string[] __arr = itm.Split('/');
int __idx = -1;
foreach (string node in __arr)
{
__idx += 1;
if (__idx == 0)
{
Node __node = new Node(node, "");
if (!__NodeList.Exists(x => x.Child == __node.Child & x.Parent == __node.Parent))
{
__node.IsRoot = true;
__NodeList.Add(__node);
}
}
else
{
Node __node = new Node(node, __arr[__idx - 1].ToString());
{
if (!__NodeList.Exists (x => x.Child == __node.Child & x.Parent == __node.Parent))
{
__NodeList.Add(__node);
}
}
}
}
}
return __NodeList;
}
anyway it's not optimised, I'm sure I can clean it up a lot..

Ratios with Datatable

I have an application that I am making an have run into a place where I got stuck. My problem is this: how can I create a ratio with a datatable.
Ex:
My Table:
|gh | gh | tf | tf|
|tf | tf | tf | tf|
|gh | gh | tf | tf|
My output:
4 gh:8 tf
Anything could be in the table, so I was hoping to use Linq to determine the ratio .. but didnt know how to find out all the diffrent results in the table.
Maybe something like this would work?
private Dictionary<String, Int32> GetCounts(DataTable dt)
{
var result = new Dictionary<String, Int32>();
foreach (DataRow row in dt.Rows)
{
foreach (var v in row.ItemArray)
{
string key = (v ?? string.Empty).ToString();
if (!result.ContainsKey(key))
{
result.Add(key, 0);
}
result[key]++;
}
}
return result;
}

Categories