Dependency Graph using Dictionaries and Lists - c#

I'm in the middle of working on a dependency graph, and I'm having trouble with properly adding my dependents and dependees.
I have it set up like:
private List<Tuple<string, string>> DG;
private Dictionary<string, List<string>> dependants;
private Dictionary<string, List<string>> dependees;
And I'm trying to add to my dictionaries like:
for (int i = 0; i < DG.Count; i++)
{
dependants.Add(DG[i].Item1, new List<string>().Add(DG[i].Item2);
}
It gives me the error "Argument2: Cannot convert from void to System.Collections.Generic.List" where I try to add to a new list in the second parameter. I think I know why I'm getting errors, but I am having trouble thinking of an alternative way to correctly add into the dictionaries.
My goal is something like this:
//DG = {("a", "b"), ("a", "c"), ("b", "d"), ("d", "d")}
// dependents("a") = {"b", "c"}
// dependents("b") = {"d"}
// dependents("c") = {}
// dependents("d") = {"d"}
// dependees("a") = {}
// dependees("b") = {"a"}
// dependees("c") = {"a"}
// dependees("d") = {"b", "d"}
So ("a", "b") means that "b" is a dependent of "a" and "a" is a dependee of "b"

Its a little longer than your code, but this might be what you need:
for (int i = 0; i < DG.Count; i++)
{
if (!dependants.ContainsKey(DG[i].Item1))
{
List<string> temp = new List<string>();
temp.add(DG[i].Item2);
dependants.Add(DG[i].Item1, temp);
}
else
dependants[DG[i].Item1].Add(DG[i].Item2);
}
Hopefully the longer code helps you understand the flow. This is only for making the dependants. Also, you were missing a bracket close in your original code:
dependants.Add(DG[i].Item1, new List<string>().Add(DG[i].Item2);
should be
dependants.Add(DG[i].Item1, new List<string>().Add(DG[i].Item2));

Related

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

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")];

How to populate KeyValuePair<int, int> inside a foreach loop

I have this code snippet as below which iterates over a split string.
if (!string.IsNullOrEmpty(profile.ContactNumber))
{
var splitContract = profile.ContactNumber.Split(new string[] { "and", "&" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var contract in splitContract)
{
//check the split if it contains "x" or "X" character - if it does contain, it means it's a valid contract
if (contract.Contains("x") || contract.Contains("X"))
{
var condensedString = contract.Replace(" ", "");
var split = condensedString.Split(new char[] { 'x', 'X' });
GetNumbersOnly(split);
}
}
}
private void GetNumbersOnly(string[] inputArray)
{
var ListKeyValuePair = new List<KeyValuePair<string, string>>();
foreach (var item in inputArray)
{
var numberToAdd = Regex.Replace(item, "[^0-9]", "", RegexOptions.None);
ListKeyValuePair.Add(?, ?);
}
}
In GetNumbersOnly method, how can I populate List of KeyValuePair inside the for each loop?
The inputArray variable has an array element of [0] = 100, [1] = 5 for the first iteration and so on.
This is the desired output for the KeyValuePair {100, 5}, {200, 10}, {500, 15}.
Sorry, I can't seem to find any related scenario when I googled it. Any help with this is greatly appreciated.
Because the key and value are stored in separate array items, your logic is dependent on order. In cases like this, you should avoid for...each and instead use plain old for, which allows you to control the manner of iteration.
private void GetNumbersOnly(string[] inputArray)
{
var ListKeyValuePair = new List<KeyValuePair<string, string>>();
for (int i=0; i< inputArray.Length; i+=2) //The "2" here is very important!
{
var numberToAdd1 = Regex.Replace(inputArray[i], "[^0-9]", "", RegexOptions.None);
var numberToAdd2 = Regex.Replace(inputArray[i+1], "[^0-9]", "", RegexOptions.None);
ListKeyValuePair.Add(new KeyValuePair<string, string>(numberToAdd1, numberToAdd2));
}
}
The ListKeyValuePair.Add( ) function is expecting 1 field which is of type KeyValuePair. You need to make one of these with a new KeyValuePair() { key = item, value = numberToAdd };
Why are you keeping key value pairs in a list? Why not a Dictionary ? Do you want duplicate pairs?

Convert jagged array of string to list of list of string in C#

I want to convert string[][] to List<List<string>>.
eg.
List<List<string>> listOfListReturned = new List<List<string>>();
string[][] twoDArrOfString = new string[2][];
I want to convert twoDArrOfString to listOfListReturned
Please suggest, how to do it?
Regards,
Vivek
Something like the following will also work:
string[][] twoDArrOfString = new string[2][];
var res = twoDArrOfString
.Where(inner => inner != null) // Cope with uninitialised inner arrays.
.Select(inner => inner.ToList()) // Project each inner array to a List<string>
.ToList(); // Materialise the IEnumerable<List<string>> to List<List<string>>
You need to handle nulls if the inner arrays have not been initialised.
If you aren't going to enumerate through all of these, you might want to drop the final ToList and simply work against the IEnumerable<List<string>> to avoid resolving all inner lists if you don't need to (taking advantage of the deferred execution of enumerables).
Any reason in particular why you are doing this?
List<List<string>> listOfListReturned = new List<List<string>>();
string[][] twoDArrOfString = new string[2][];
twoDArrOfString[0] = new[] {"a", "b"};
twoDArrOfString[1] = new[] {"c", "d"};
foreach (var s in twoDArrOfString)
{
listOfListReturned.Add(new List<string>(s));
}
Or
var result = twoDArrOfString.ToList();
var listOfList = result.Select(x => x.ToList()).ToList();
Not tested just imagine it :)
for (int i = 0; i < twoDArrOfString.Length; i++)
{
for (int j = 0; j < twoDArrOfString[i].Length; j++)
{
listOfListReturned[i].Add(twoDArrOfString[j].ToString());
}
listOfListReturned.Add(listOfListReturned[i]);
}
A more linq-ish version might be
List<List<string>> listOfListReturned = new List<List<string>>()
{
new List<string> {"A", "B"},
new List<string> {"C", "D"},
};
string[][] twoDArrOfString = new string[2][]
{
new [] {"A", "B"},
new []{"C", "D"},
};
var actual = twoDArrOfString.Select(arr => new List<string>(arr)).ToList();
for (var idx = 0; idx < listOfListReturned.Count; idx++) {
CollectionAssert.AreEqual(listOfListReturned[idx], actual[idx]);
}
EDIT: Adam's solution above allowing for empty rows is better.

Identify where I am in an array

I have a string array in C# like so:
string[] sites = new string[] {
"http://foobar.com",
"http://asdaff.com",
"http://etc.com"
};
I'm using this array in a foreach and I would like to be able to add a "type" value of 1, 2, or 3, depending on which site I am currently iterating through. I am concatenating data with the StringBuilder from these sites. Now, I could store the site as a varchar, but it would be really neat, since this array will NEVER change to associate a number with the string and build it that way.
Use for loop instead of foreach:
for(int i = 0; i < sites.Length; i++)
{
// use sites[i]
}
LINQ's Select can be used to project an index onto a collection.
sites.Select((x, n) => new { Site = x, Index = n })
You can use a dictionary for this - Dictionary<int, string> (or Dictionary<string, int>).
var sitesWithId = new Dictionary<string, int>
{
new { "http://foobar.com", 1},
new { "http://asdaff.com", 2},
new { "http://etc.com", 3}
}
Another option is to just use a List<string> and IndexOf to find out the index.
var sites = new List<string> {
"http://foobar.com",
"http://asdaff.com",
"http://etc.com"
};
var foobarIndex = sites.IndexOf("http://foobar.com");
A third option, using the static IndexOf methods of Array and not changing your array at all:
var foobarIndex = Array.IndexOf(sites, "http://foobar.com");
Try with for loop;
for(int i = 0; i < sites.Length; i++)
{
Console.WriteLine(sites[i]);
}
using for elements of sites[] array like this;
sites[1]
sites[2]
sites[3]
or you can use Dictionary<TKey, TValue> as Oded suggest.

Tricky algorithm... finding multiple combinations of subsets within nested HashSets?

I have a problem where I have to find multiple combinations of subsets within nested hashsets. Basically I have a "master" nested HashSet, and from a collection of "possible" nested HashSets I have to programmatically find the "possibles" that could be simultaneous subsets of the "master".
Lets say I have the following:
var master = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B", "C"}),
new HashSet<string>( new string[] { "D", "E"}),
new HashSet<string>( new string[] { "F"})
}
);
var possible1 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B", "C"}),
new HashSet<string>( new string[] { "F"})
}
);
var possible2 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "D", "E"})
}
);
var possible3 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "F"})
}
);
var possible4 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "X", "Y", "Z"})
}
);
var possible5 = new HashSet<HashSet<string>>(new HashSet<string>[] {
new HashSet<string>( new string[] { "A", "B" }),
new HashSet<string>( new string[] { "D", "E"})
}
);
The output I should get from my algorithm should be as follows:
All possible combination subsets:
possible1 and possible2
possible3 and possible5
possible2 and possible3
possible1
possible2
possible3
possible5
I'm trying to figure out the best way to approach this. There is, of course, the brute force option, but I'm trying to avoid that if I can.
I just hope my question was clear enough.
EDIT
To further elaborate on what constitutes a subset, here are some examples, given the master {{"A","B","C"},{"C","D","E",F"},{"X","Y","Z"}} :
{{"A","B"}{"C","D"}} would be a subset of
{{"A","B","C"},{"X","Y"}} would be a subset
{{"A","B"},{"A","B"}} would NOT be a subset
{{"A","B","C","D"}} would NOT be a subset
{{"A","B","C"},{"C","D","X"}} would NOT be a subset
Basically each child set needs to be a subset of a corresponding child in the master.
Use bruteforce:
public static int IsCsInMaster(HashSet<string> childSubset, List<HashSet<string>> master, int startIndex)
{
for (int i = startIndex; i < master.Count; i++)
if (childSubset.IsSubsetOf(master[i])) return i;
return -1;
}
public static bool IsChildInMaster(List<HashSet<string>> child, List<HashSet<string>> master)
{
foreach (var childSubset in child) if (IsCsInMaster(childSubset, master, 0) == -1) return false;
return true;
}
public static bool IsChildInMasterMulti(List<HashSet<string>> child, List<HashSet<string>> master)
{
Dictionary<int, int> subsetChecker = new Dictionary<int, int>();
List<IEnumerable<int>> multiMatches = new List<IEnumerable<int>>();
int subsetIndex;
// Check for matching subsets.
for (int i = 0; i < child.Count; i++)
{
subsetIndex = 0;
List<int> indexes = new List<int>();
while ((subsetIndex = IsCsInMaster(child[i], master, subsetIndex)) != -1)
{
indexes.Add(subsetIndex++);
}
if (indexes.Count == 1)
{
subsetIndex = indexes[0];
if (subsetChecker.ContainsKey(subsetIndex)) return false;
else subsetChecker[subsetIndex] = subsetIndex;
}
else
{
multiMatches.Add(indexes);
}
}
/*** Check for multi-matching subsets. ***/ //got lazy ;)
var union = multiMatches.Aggregate((aggr, indexes) => aggr.Union(indexes));
// Filter the union so only unmatched subset indexes remain.
List<int> filteredUion = new List<int>();
foreach (int index in union)
{
if (!subsetChecker.ContainsKey(index)) filteredUion.Add(index);
}
return (filteredUion.Count >= multiMatches.Count);
}
And in code:
IsChildInMasterMulti(possible2, master)
The code does not handle the {{"A","B"},{"A","B"}} case, though. That is a LOT more difficult (flagging used subsets in master, maybe even individual elements - recursively).
Edit2: The third method handles the {{"A","B"},{"A","B"}} case as well (and more).
Use the simplest solution possible.
Keep in mind that if someone else has to look at your code they should be able to understand what it's doing with as little effort as possible. I already found it hard to understand from your description what you want to do and I haven't had to read code yet.
If you find that it's too slow after it's working optimize it then.
If possible write unit tests. Unit tests will ensure that your optimized solution is also working correctly and will help others ensure their changes don't break anything.

Categories

Resources