Populating a 2D Matrix of List<string> from csv - c#

I have a matrix of routes stored into a csv from a pandas dataframe. The content of this csv looks like:
,hA,hB,hC
hA,[],["hA","hB"],["hA","hB","hC"]
hB,["hB","hA"],[],["hB","hC"]
hC,["hC","hB","hA"],["hC","hB"],[]
From this file I would like to generate a matrix in c#, so I could get the route from hA to hC with something like:
routes["hA"]["hC"]
I can achieve this generating manually a Dictionary<string, Dictionary<string, List<string>>> like:
Dictionary<string, Dictionary<string, List<string>>> routes = new Dictionary<string, Dictionary<string, List<string>>>(){
{"hA", new Dictionary<string, List<string>>(){ { "hA", new List<string>() }, {"hB", new List<string>() { "hA", "hB" }}, { "hC", new List<string>() { "hA", "hB", "hC" }}}},
{ "hB", new Dictionary<string, List<string>>() { { "hA", new List<string>() { "hB", "hA" }}, { "hB", new List<string>() { }, { "hC", new List<string>() { "hB", "hC" }}}},
{ "hC", new Dictionary<string, List<string>>() { { "hA", new List<string>() { "hC", "hB", "hA" }}, { "hB", new List<string>() { "hC", "hB" }}, { "hC", new List<string>() { } }}}
};
But in case the size of the matrix increases or everytime a route changes it is a lot of reworking involved. Thant is why I could like to populate the routes matrix from the csv directly
Is there any way of populating this matrix from a csv? or is it a better type of collections to store this routes instead of Dictionary<string, Dictionary<string, List<string>>>?

Oof.. I think I'd read that CSV with a parser library set to use [ and ] as "quote" chars, but this will read it simplistically:
var lines = File.ReadAllLines(path);
var cols = lines[0].Split(',');
var frame = new Dictionary<string, Dictionary<string, string[]>>();
foreach(var line in lines.Skip(1)){
var bits = line.Replace("]", "").Split(",[");
var row = bits[0];
for(int i = 1; i < cols.Length; i++){
var col = cols[i];
frame.TryAdd(row, new Dictionary<string, string[]>());
frame[row][col] = bits[i].Split(',').Select(s => s.Trim('"')).ToArray();
}
}
That should deliver you your nested dictionaries so you can address them like you would a dataframe.. By the way, I don't know what happens if you ask a dataframe for something that isn't there, but c# would throw a KeyNotFoundException if you asked for eg frame["hZ"]["hello"] ..
If you want the innermost storage container to be a List you can swap the ToArray to be ToList
You perhaps don't need to nest, by the way:
var frame = new Dictionary<(string, string), string[]>();
foreach(var line in lines.Skip(1)){
var bits = line.Replace("]", "").Split(",[");
var row = bits[0];
for(int i = 1; i < cols.Length; i++){
var col = cols[i];
frame[(row, col)] = bits[i].Split(',').Select(s => s.Trim('"')).ToArray();
}
}
It could be queried like frame[("hA","hB")]

Turn your node names (ie, hA, hB, hC) into an enum:
enum Indexer {
hA = 0,
hB = 1,
hC = 2
}
Use a two-dimensional array of lists:
List<string>[,] Matrix = new List<string>[3,3];
Access the data out of the Matrix:
List<string> path = Matrix[(int)Indexer.hA, (int)Indexer.hC];
If you need to, you can convert the text-based node names back to an enum:
var n = (Indexer)Enum.Parse(typeof(Indexer), "hA");
This assumes that you are importing a csv of pre-defined node names. Let me know if the node names can't be pre-defined and I'll update the answer.

Based on #CaiusJard and #derHugo suggestions
I needed to modify a little bit the original csv file to make it easier by removing the first column (which cointaed the index) and using ";" as column separator df_routes.to_csv("routes_mtrx_headers.csv", sep = ';', index = False)
The final solution is
var route_dictionary = new Dictionary<(string, string), string[]>();
using (var reader = new StreamReader(#"C:/mypath/routes_mtrx_headers.csv"))
{
string[] locations = reader.ReadLine().Split(';');
int rowIdx = 0;
int colIdx = 0;
while (!reader.EndOfStream)
{
var row = reader.ReadLine();
var columns = row.Split(';');
colIdx = 0;
foreach (var col in columns)
{
// Removing the List wrapper
var path = col.Replace("]", "").Replace("[", "").Split(',');
route_dictionary.Add((locations[colIdx], locations[rowIdx]), path);
colIdx += 1;
}
rowIdx += 1;
}
}
// Finally to access each element in the matrix
var route = route_dictionary[("hA", "hC")];

Related

Cartesian product with checking condition when adding a new element to the set and without a repeat of elements

There are two sets of:
int Transformer substations
object Buildings(the set is larger than the first set at least by 5 times).
The building has 2 parameters (number and load).
Need to create all possible combinations: each transformer station is loaded by 60-80% in every combination, and buildings don't repeat.
Glad to hear any suggestions.
Tried the Cartesian product but I have no idea how to apply it. Ideas just don't appear. I guess it is because of the stress produced by the war in Ukraine where I live.
A cartesian product without elements repetition in any given result set and without permutations:
static void combinations(List<List<string>> srs, int[] size, List<string> curr, int index)
{
if (index == srs.Count())
{
int s = curr.Count();
string[] d = new string[s];
List<string> x = d.ToList();
x.AddRange(curr);
x.RemoveAll(item => item == null);
dest.Add(x);
string res = "";
foreach (var item in x)
{
res += item + ", ";
}
//dest.add(d);
Console.WriteLine(res);
}
else
{
for (int i = 0; i < size[index]; i++)
{
int n = size[index];
string[] dim = new string[n];
dim = srs[index].ToArray();
curr.Add(dim[i]);
index++;
combinations(srs, size, curr, index);
index--;
curr.RemoveAt(curr.Count() - 1);
}
}
}
public static void init()
{
string[] confidence = new string[] { "High", "Low Variable" };
string[] goalspec = new string[] { "High", "Low Some" };
string[] quality = new string[] { "High", "Low Variable" };
string[] tansSkills = new string[] { "High", "Low some" };
string[] sitLead = new string[] { "S1", "S2", "S3", "S4" };
string[] devLev = new string[] { "D1", "D2", "D3", "D4" };
string[] statReason = new string[] {"In backlog", "Canceled", "Completed" };
List<List<string>> srs = new List<List<string>>
{
confidence.ToList(),
goalspec.ToList(),
quality.ToList(),
tansSkills.ToList(),
sitLead.ToList(),
devLev.ToList(),
statReason.ToList()
};
number_elem = srs.Count();
int[] size = new int[number_elem];
size[0] = confidence.Count();
size[1] = goalspec.Count();
size[2] = quality.Count();
size[3] = tansSkills.Count();
size[4] = sitLead.Count();
size[5] = devLev.Count();
size[6] = statReason.Count();
dest = new List<List<string>>();
List<string> curr = new List<string>();
combinations(srs, size, curr, 0);
}

C# convert dictionary into CSV like string

I have a dictionary Dictionary<string, List<string>> I want order it alphabetically by the keys and convert it into a string that can be written into a CSV file with the keys as column headers and the values as values for that column.
My onordered dictionary looks like:
{
"Name" : ["John", "Ciara", "Moses"],
"Age" : ["23", "16", "37"],
"State" : ["Alabama", "Florida", "New York"]
}
The end result will look like:
Age,Name,State
23,John,Alabama
16,Ciara,Florida
37,Moses,New York
Please how I can achieve this in C#?
For clarity, here is a link to what the task entail.
And below is my approach of solving it. I converted the string into a dictionary with the column headings as keys. My problem now is converting the dictionary back to the string format.
public static string SortCsvColumns( string csv_data )
{
var data = csv_data.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
var values = data.Skip(1).ToArray();
var splittedValues = new List<List<string>>();
var dataSet = data[0].Split(new string[] {","}, StringSplitOptions.None).ToDictionary(x => x, x => new List<string>());
for(int i = 0; i < values.Length; i++)
{
splittedValues.Add(values[i].Split(new string[] { "," }, StringSplitOptions.None).ToList());
}
for(int i = 0; i < splittedValues.Count(); i++) {
var splittedValue = splittedValues[i];
for(int j = 0; j < splittedValue.Count(); j++) {
dataSet.Values.ElementAt(i).Add(splittedValue[j]);
}
}
dataSet = dataSet.OrderBy(key => key.Key);
}
Can someone suggest the best approach to do this please.
First, create a map to re-order the columns in sorted order by column header:
var map = new StringReader(csv_data).ReadLine() // get header line
.Split(';') // split into array of headers
.Select((h, n) => new { header = h, OrigPos = n }) // remember original position
.OrderBy(hn => hn.header, StringComparer.CurrentCultureIgnoreCase) // sort into new position
.Select(hn => hn.OrigPos) // return just old position new new order
.ToList();
Then remap each CSV line into the new order and recombine into a string:
using var sr = new StringReader(csv_data);
var ans = String.Join("\n",
sr.ReadLines()
.Select(line => line.Split(';'))
.Select(columns => String.Join(";", map.Select(pos => columns[pos]))));
This requires an extension method on TextReader to enumerate the lines of a TextReader:
public static class StringReaderExt {
public static IEnumerable<string> ReadLines(this TextReader sr) {
string line;
while ((line = sr.ReadLine()) != null)
yield return line;
}
}

Add Key in Dictionary dynamically

Below is the code where key is being hard-coded in Dictionary
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ "ID", Convert.ToString(dt.Rows[i]["ID"]) },
{ "STATUS", Convert.ToString(dt.Rows[i]["Name"]) },
{ "TYPE", Convert.ToString(dt.Rows[i]["TYPE"]) }
};
datalist.Add(data);
}
Now, instead of hard-coding the keys like ID, STATUS, etc, I want to add it from my string array containing the values below
string[] arrNames = ConfigurationManager.AppSettings["NameKey"].Split(',');
How can I traverse arrNamesto add keys in Dictionary and then add in List?
Iterate through the collection of names:
var datalist = new List<IDictionary<string, string>>();
string[] arrNames = ConfigurationManager.AppSettings["NameKey"].Split(',');
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>();
foreach (var name in arrNames)
{
data[name] = Convert.ToString(dt.Rows[i][name]);
}
datalist.Add(data);
}
your code should look something like this
var datalist = new List<IDictionary<string, string>>();
string[] arrNames = Convert.ToString(ConfigurationManager.AppSettings["NameKey"]).Split(',');
if (arrNames.Length == 3)
{
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ arrNames[0], Convert.ToString(dt.Rows[i][arrNames[0]]) },
{ arrNames[1], Convert.ToString(dt.Rows[i][arrNames[1]]) },
{ arrNames[2], Convert.ToString(dt.Rows[i][arrNames[2]]) }
};
datalist.Add(data);
}
}
You can use linq method ToDictionary. Try this code:
string[] arrNames = // new[] {"ID", "STATUS", "TYPE"};
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
datalist.Add(
arrNames
.Select(key =>
new
{
key,
value = Convert.ToString(dt.Rows[i][key])
}
)
.ToDictionary(x => x.key, x => x.value)
);
If you prefer LINQ-y and concise you could try something like:
var names = ConfigurationManager.AppSettings["NameKey"].Split(',');
var list = dt.AsEnumerable()
.Select(r => names.ToDictionary(n => n, n => r[n]))
.ToList();
Here I'm assuming dt is a DataTable.
If you have at least the same number of items in your arrNames array than columns you want to read and of course with this order, then you can hardcore the indexes.
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ arrNames[0], Convert.ToString(dt.Rows[i]["ID"]) },
{ arrNames[1], Convert.ToString(dt.Rows[i]["Name"]) },
{ arrNames[2], Convert.ToString(dt.Rows[i]["TYPE"]) }
};
datalist.Add(data);
}

create dynamic Keyboard telegram bot in c# , MrRoundRobin API

I want to create custom keyboard in telegram.bot
For example:
We have an array of string that gets from the database or other recurses
how we can push data from the array to InlineKeyboardMarkup in for loop or function
//array of Button
string[] ButtonItem= new string[] { "one", "two", "three", "Four" };
//function or solution to create keyboard like this
var keyboard = new InlineKeyboardMarkup(new[]
{
new[]
{
new InlineKeyboardButton("one"),
new InlineKeyboardButton("two"),
},
new[]
{
new InlineKeyboardButton("three"),
new InlineKeyboardButton("Four"),
}
});
You could use a separate function to get an array of InlineKeyboardButton
private static InlineKeyboardButton[][] GetInlineKeyboard(string [] stringArray)
{
var keyboardInline = new InlineKeyboardButton[1][];
var keyboardButtons = new InlineKeyboardButton[stringArray.Length];
for (var i = 0; i < stringArray.Length; i++)
{
keyboardButtons[i] = new InlineKeyboardButton
{
Text = stringArray[i],
CallbackData = "Some Callback Data",
};
}
keyboardInline[0] = keyboardButtons;
return keyboardInline;
}
And then call the function:
var buttonItem = new[] { "one", "two", "three", "Four" };
var keyboardMarkup = new InlineKeyboardMarkup(GetInlineKeyboard(buttonItem));
Create InlineKeyboardMarkup in a method:
public static InlineKeyboardMarkup InlineKeyboardMarkupMaker(Dictionary<int, string> items)
{
InlineKeyboardButton[][] ik = items.Select(item => new[]
{
new InlineKeyboardButton(item.Key, item.Value)
}).ToArray();
return new InlineKeyboardMarkup(ik);
}
Then use it like this:
var items=new Dictionary<int,string>()
{
{0 , "True" }
{1 , "False" }
};
var inlineKeyboardMarkup = InlineKeyboardMarkupMaker(items);
Bot.SendTextMessageAsync(message.Chat.Id, messageText, replyMarkup: inlineKeyboardMarkup);
Selecting True or False make an update with Update.CallbackQuery.Dataequal to selected item key (0 or 1).
Create InlineKeyboardButton with specific columns by below method.
public static IReplyMarkup CreateInlineKeyboardButton(Dictionary<string, string> buttonList, int columns)
{
int rows = (int)Math.Ceiling((double)buttonList.Count / (double)columns);
InlineKeyboardButton[][] buttons = new InlineKeyboardButton[rows][];
for (int i = 0; i < buttons.Length; i++)
{
buttons[i] = buttonList
.Skip(i * columns)
.Take(columns)
.Select(direction => new InlineKeyboardCallbackButton(
direction.Key, direction.Value
) as InlineKeyboardCallbackButton)
.ToArray();
}
return new InlineKeyboardMarkup(buttons);
}
Use method like this:
public static IReplyMarkup CreateInLineMainMenuMarkup()
{
Dictionary<string, string> buttonsList = new Dictionary<string, string>();
buttonsList.Add("one", "DATA1");
buttonsList.Add("two", "DATA2");
buttonsList.Add("three", "DATA3");
return CreateInlineKeyboardButton(buttonsList, 2);
}
Thanks pouladpld to create this function.

dictionary of lists in c#(cant seem to get my lists)

Here i used linq to filter my result in an array and pass into a list and from that list into a dictionary as you can see below
//array is a multidimensional array with string data in it
var datumn = array;
var list = new List<string>();
var stringcounts = new Dictionary<int,List<string>>();
var listtemp = new List<string>();
//linq
var arrayresult = from string a in datumn where a != "FREE" select a;
//adding result from arrayresult to list
foreach (var listing in arrayresult)
{
list.Add(listing);
}
//using linq again i filter my list then add to dictionary
for (int count = 3; count > 0; count-- )
{
var duplicateItems = from x in list
group x by x into grouped
where grouped.Count() == count
select grouped.Key;
foreach (var replace in duplicateItems)
{
listtemp.Add(replace.ToString());
}
stringcounts.Add(count, lists);
//clearing the list to avoid duplicating data in my dictionary
listtemp.Clear();
}
for (int key = stringcounts.Count; key > 0; --key)
{
var holding = stringcounts[key];
foreach (var li in holding)
{
MessageBox.Show(li.ToString());
//just view what i have to check if the data is correct
}
}
`
the program skips iterator over of the lists and ends can some one help with this
and i have tried everything including linq and other collections like hashtable
and maps but nothing works and it is not a console application
This line is wrong:
var dict = new Dictionary<int, List<string>>();
Remove the ";".
Result:
indigo silver violet purple green pink red brown yellow
Edit: full code for comparison:
var dict = new Dictionary<int, List<string>>()
{
{1, new List<string>{"red", "brown", "yellow"}},
{2, new List<string>{"purple", "green", "pink"}},
{3, new List<string>{"indigo", "silver", "violet"}}
};
// now i want to get my values from the lists in the dictionary
for (int count = 3; count > 0; count--)
{
var l = dict[count];
foreach (var li in l)
{
li.Dump();
}
}
foreach (var item in dict)
{
var list = item.Value;
foreach (var str in list)
{
MessageBox.Show(str);
}
}
The listtemp.Clear() is a bad syntax so therefore it should be removed and the listtemp should be declared in the for loop therefore removing redundancy and the initial problem

Categories

Resources