C# Variable not getting all values outside for loop - c#

I have two values in the dictionary but when I try to get the two values outside the loop I am only getting one value. The locationdesc variable value are being overwritten. Is there a better way to create unique variables to handle this issues
There are two keys location-1 and location-2. I am trying to figure out how to get both the values outside the loop. Am I doing it wrong?
string locationDesc = "";
string locationAddress = "";
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
for (int i = 1; i <= count; i++)
{
if (dictionary.ContainsKey("location-"+i))
{
string locationData = dictionary["location-"+i];
string[] locationDataRow = locationData.Split(':');
locationDesc = locationDataRow[0];
locationAddress = locationDataRow[1];
}
}
// Only getting location-2 value outside this loop since locationDesc is not unique.
Debug.WriteLine("Location Desc from dictionary is : " + locationDesc);
Debug.WriteLine("Location Add from dictionary is : " + locationAddress);
What I would like to get here is get both the values like locationDesc1 and locationDesc2 instead of locationDesc
What I am looking for is to create locationDesc and locationAddress unique so I can access both the values outside the for loop.
More Explanation as I was not very clear:
I have a dynamic table that will be created in the front end. Every time a location is created I create a cookie. For e.g. location-1, location-2 ...location-n with the location description and location values as values in the cookie. I am trying to access these values in the backend by creating a dictionary so I can assign all the values to unique variable which will make it easier for me to pass these values to a api call. I think I am over complicating a simple issue and might be doing it wrong.
My api call will be something like this:
<field="" path="" value=locationDesc1>
<field="" path="" value=locationDesc2>

The problem with your loop is that you are relying on the position of the entry in the dictionary matching the index within your loop. Your first line of code pretty much has it though:
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
What this tells me is that you are looking for all entries in your dictionary where the key starts with "location-". So why not do that directly:
var values = dictionary.Where(d => d.Key.StartsWith("location-"));
And to do the extraction/string splitting at the same time:
var values = dictionary
.Where(d => d.Key.StartsWith("location-"))
.Select(d => d.Item.Split(':')
.Select(s => new
{
LocationDesc = s[0],
LocationAddress = s[1]
});
This will give you an IEnumerable of LocationDesc/LocationAddress pairs which you can loop over:
foreach(var pair in values)
{
Debug.WriteLine(pair.LocationDesc);
Debug.WriteLine(pair.LocationAddress);
}

Try this:
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
Dictionary<string, string> values = new Dictionary<string, string>();
for (int i = 1; i <= count; i++)
{
if (dictionary.ContainsKey("location-"+i))
{
string locationData = dictionary["location-"+i];
string[] locationDataRow = locationData.Split(':');
values.Add(locationDataRow[0],locationDataRow[1]);
}
}
foreach (var item in values)
{
Debug.WriteLine(item.Key + " : " + item.Value);
}

As you are dealing with multiple values, you should go with a container where you can store all the values.
if you are dealing with only two unique values then use below code.
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
string[] locationDesc = new string[2];
string[] locationAddress = new string[2];
for (int i = 1; i <= count; i++)
{
if (dictionary.ContainsKey("location-"+i))
{
string locationData = dictionary["location-"+i];
string[] locationDataRow = locationData.Split(':');
locationDesc[i-1] = locationDataRow[0];
locationAddress[i-1] = locationDataRow[1];
}
}
for (int i = 0; i <= locationDesc.Length-1; i++)
{
Debug.WriteLine("Location Desc from dictionary is : " + locationDesc[i]);
Debug.WriteLine("Location Add from dictionary is : " + locationAddress[i]);
}
if number of unique values is not fixed then go with ArrayList
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
ArrayList locationDesc = new ArrayList();
ArrayList locationAddress = new ArrayList();
for (int i = 1; i <= count; i++)
{
if (dictionary.ContainsKey("location-"+i))
{
string locationData = dictionary["location-"+i];
string[] locationDataRow = locationData.Split(':');
locationDesc.Add(locationDataRow[0]);
locationAddress.Add(locationDataRow[1]);
}
}
for (int i = 0; i < locationDesc.Count; i++)
{
Debug.WriteLine("Location Desc from dictionary is : " + locationDesc[i]);
Debug.WriteLine("Location Add from dictionary is : " + locationAddress[i]);
}
Simple One. If you only want to show result using Debug.WriteLine, then go with below code
int count = dictionary.Count(D => D.Key.StartsWith("location-"));
for (int i = 1; i <= count; i++)
{
if (dictionary.ContainsKey("location-"+i))
{
string locationData = dictionary["location-"+i];
string[] locationDataRow = locationData.Split(':');
Debug.WriteLine("Location Desc from dictionary is : " + locationDataRow[0]);
Debug.WriteLine("Location Add from dictionary is : " + locationDataRow[1]);
}
}
Not able to prepare Code in Visual Studio at the moment therefore there may be some syntax errors.

It is hard to judge what you are event trying to do. I would not just be dumping objects you already have into other objects for fun. If you are just trying to expose values in a loop for use with another function, you can just use LINQ to iterate over the dictionary. If you want a specific value just add a where LINQ expression. LINQ should be in any .NET framework after 3.5 I believe.
public static void ApiMock(string s)
{
Console.WriteLine($"I worked on {s}!");
}
static void Main(string[] args)
{
var d = new Dictionary<int, string> {
{ 1, "location-1" },
{ 2, "location-2" },
{ 3, "location-3" }
};
d.ToList().ForEach(x => ApiMock(x.Value));
//I just want the second one
d.Where(x => x.Value.Contains("-2")).ToList().ForEach(x => ApiMock(x.Value));
//Do you want a concatenated string
var holder = string.Empty;
d.ToList().ForEach(x => holder += x.Value + ", ");
holder = holder.Substring(0, holder.Length - 2);
Console.WriteLine(holder);
}

Related

Adding new objects to a dictionary while iterating through a loop?

I've been trying to add objects with specific properties to a Dictionary<int, DwgObject> dictionary in a while loop by reading through values in anexcel column.
It seems even though I'm declaring a new object in my loop, the properties of my respective objects end up being the same throughout the dictionary.
Here's the while loop I'm using:
Dictionary<int, DrawingObj> lst = new Dictionary<int, DrawingObj>();
int numRows = 0;
//Loop through Drawing Number column -> Add new obj to list with drawing number prop
//Iterate to next row down -> check for empty string to break loop
string current = wks.Cells[row, column]?.Value2?.ToString() ?? "";
while ((current != ""))
{
DrawingObj newDWG = new DrawingObj();
newDWG.dwgNumber = current;
lst.Add(numRows, newDWG);
numRows++;
current = wks.Cells[(row + numRows), column].Text;
}
The resulting dictionary gives me 13 entries of objects with the property dwgNumber equal to the string of my last excel value. How can I store the unique property values for key-value pair?
Update: I tried changing the loop to pull out the properties of each object as a single string and storing to an array, then later on, using a loop to create an object instance with all of the properties at once. But this results in the same error. See below:
//store string of all properties with delimiter '|'
while ((current != ""))
{
StringBuilder properties = new StringBuilder();
for(int i = 0; i < 7; i++)
{
properties.Append(wks.Cells[(row + numRows), propColIndex[i]].Text);
if(i != propColIndex.Length - 1) { properties.Append("|"); }
}
propStrings.Add(properties.ToString());
numRows++;
current = wks.Cells[(row + numRows), column].Text;
}
//for each property string create new object w/ props -> add object to dictionary foreach (var props in propStrings) // t i = 1; i <= numRows-1; i++)
{
percent = (j / numRows) * 60;
int k = 0;
string[] values = new string[8];
foreach (string prop in props.Split(delimiter))
{
values[k] = prop;
k++;
}
DrawingObj newDWG = new DrawingObj()
{
dwgNumber = values[0],
sheetNo = values[1],
revNo = values[2],
keyWord = values[3],
dwgType = values[4],
descSublocation = values[5],
revType = values[6],
fileName=values[7],
facilityLoc = constFacilFields[0],
facilityType = constFacilFields[1],
jobOrderNum = constFacilFields[2]
};
lst.Add(j-1, newDWG);
j++;

How to efficiently declare objects [duplicate]

I was wondering whether there's a way in a "for" loop to assign a value to a string variable named according to its index number?
let's say I have 3 string variables called:
string message1 = null;
string message2 = null;
string message3 = null;
And I want the 'for' loop to do the something like the following code:
for (int i = 1; i <=3; i++)
{
messagei = "blabla" + i.ToString();
}
I don't want to use an "if" or a "switch" because it will make the code harder to follow.
Is there a way to do that?
You don't want 3 variables with the same name, you want an array of those variables.
string[] messages = new string[3]; // 3 item array
You can then store your items in the array elements
messages[0] = "Apple"; // array index starts at 0!
messages[1] = "Banana";
messages[2] = "Cherry";
Another way to create that array is an inline array initializer, saves some code
string[] messages = { "Apple", "Banana", "Cherry" };
(Note: there are more valid syntaxes for array initialization. Research on the various other methods is left as an exercise.)
And access them via a loop (foreach)
foreach (string fruit in messages)
{
Console.WriteLine("I'm eating a " + fruit);
}
Or for
for (int i = 0; i < messages.Length; i++)
{
Console.WriteLine("I'm eating a " + messages[i]); // reading the value
messages[i] = "blabla" + i.ToString(); // writing a value to the array
}
can you use an array? or list type?
string[] messages = new string[3];
for (int i = 1; i <=3; i++)
{
messages[i] = "blabla" + i.ToString();
}
You said you don't want to have a switch statement. I realize this does have a switch, but if you must have three different variables, you could encapsulate your switch inside a function call:
string message1 = null;
string message2 = null;
string message3 = null;
void SetMessage(int i, string value)
{
if(i == 1)
message1 = value;
etc
}
for (int i = 1; i <=3; i++)
{
SetMessage(i, "blabla" + i.ToString());
}
Not an optimal solution but if you MUST have separate variables it will hide the mess.
You can't do that (well, not sanely). Have you considered using an array of strings instead?
I think you should use an array for this kind of variables.
string[] message = new string[3];
for (int i = 1; i <=3; i++)
{
message[i] = "blabla" + i.ToString();
}
Usually instead of having N differents variables named 1, 2, ..., N the way is to store them in an array:
string message[3];
message[0] = null;
message[1] = null;
message[2] = null;
and then the loop:
for (int i = 0; i <=2; i++)
{
message[i] = "blabla" + i.ToString();
}
Note that, usually again, a set of indexed variables starts with value 0 ;)
I would go about it a little differently, maybe use a dictionary and store your messages. Something like this:
Dictionary<string, string> messages = new Dictionary<string, string>();
for(int i = 1; i <= 3; i++)
{
messages.Add("message" + i.ToString(), i.ToString());
}
You can also do it without the index:
string[] Messages = { "Tom", "Dick", "Harry" };
foreach (String Message in Messages)
{
Response.Write("Hello " + Message + "<br />");
}
If you declare your variable in a class as public variables, you can access them as follow;
public partial class _Default : System.Web.UI.Page
{
public string message1 = null;
public string message2 = null;
public string message3 = null;
public void setVars()
{
for (int i = 1; i <=3; i++)
{
this.GetType().GetField("message" + i.ToString()).SetValue(this, "blabla" + i.ToString());
}
}
}

Quickest algorithm for identifying pairs in collection of string

I am looking for the quickest algorithm:
GOAL: output the total number of pair occurrences found on a line. The individual elements may be in any order on any given line.
INPUT:
a;b;c;d
a;e;f;g
a;b;f;h
OUTPUT
a;b = 2
a;c = 1
a;d = 1
a;e = 1
a;f = 2
a;g = 1
b;c = 1
b;d = 1
I am programming in C#, I've got a nested for loop adding do a common dictionary of type where string is like a;b and when an occurrence is found it adds to the existing int tally or adds a new one at tally = 0.
Note this:
a;b = 1
b;a = 1
Should be reduced to this:
a;b = 1
I am open to using other languages, the output is in a plain text file which I feed into Gephi visualization tool.
Bonus: Very interested to know the name of this particular algorithm if it's out there. Pretty sure it is.
String[] data = File.ReadAllLines(#"C:\input.txt");
Dictionary<string, int> ress = new Dictionary<string, int>();
foreach (var line in data)
{
string[] outStrings = line.Split(';');
for (int i = 0; i < outStrings.Count(); i++)
{
for (int y = 0; y < outStrings.Count(); y++)
{
if (outStrings[i] != outStrings[y])
{
try
{
if (ress.Any(x => x.Key == outStrings[i] + ";" + outStrings[y]))
{
ress[outStrings[i] + ";" + outStrings[y]] += 1;
}
else
{
ress.Add(outStrings[i] + ";" + outStrings[y], 0);
}
}
catch (Exception)
{
}
}
}
}
}
foreach (var val in ress)
{
Console.WriteLine(val.Key + "----" + val.Value);
}
I think your inner loop should start with i + 1 instead of starting back at 0 again, and the outer loop should only run until Length - 1, since the last item will be compared on the inner loop. Also, when you add a new item, you should add the value 1, not 0 (since the whole reason we're adding it is because we found one).
You can also just store the key into a string once instead of doing multiple concatenations during your comparison and assignment, and you can use the ContainsKey method to determine if a key exists already.
Also, you might want to consider avoiding empty catch blocks unless you're really certain that you don't care if or what went wrong. If I'm expecting an exception and know how to handle it, then I catch that exception, otherwise I'll just let it bubble up the stack.
Here's one way you could modify your code to find all pairs and their counts:
Update
I added a check to ensure that the "pair" key is always sorted, so that "b;a" becomes "a;b". This wasn't an issue in your sample data, but I extended the data to include lines like b;a;a;b;a;b;a;. Also I added StringSplitOptions.RemoveEmptyEntries to the Split method to handle cases where a line begins or ends with a ; (otherwise the null value resulted in a pair like ";a").
private static void Main()
{
var data = File.ReadAllLines(#"f:\public\temp\temp.txt");
var pairCount = new Dictionary<string, int>();
foreach (var line in data)
{
var lineItems = line.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
for (var outer = 0; outer < lineItems.Length - 1; outer++)
{
for (var inner = outer + 1; inner < lineItems.Length; inner++)
{
var outerComparedToInner = string.Compare(lineItems[outer],
lineItems[inner], StringComparison.Ordinal);
// If both items are the same character, ignore them and keep looping
if (outerComparedToInner == 0) continue;
// Create the pair such that the lower of the two
// values is first, so that "b;a" becomes "a;b"
var thisPair = outerComparedToInner < 0
? $"{lineItems[outer]};{lineItems[inner]}"
: $"{lineItems[inner]};{lineItems[outer]}";
if (pairCount.ContainsKey(thisPair))
{
pairCount[thisPair]++;
}
else
{
pairCount.Add(thisPair, 1);
}
}
}
}
Console.WriteLine("Pair\tCount\n----\t-----");
foreach (var val in pairCount.OrderBy(i => i.Key))
{
Console.WriteLine($"{val.Key}\t{val.Value}");
}
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
Given a file containing your sample data, the output is:
#mrmcgreg, finally after changing the implementation to the ECLAT algorythm everything runs in seconds instead of hours.
Basically for each unique tag, keep track of the LINE NUMBERS where those tags are found, and simply intersect the pair of list of numbers by combination pairs to get the count.
Dictionary<string, List<int>> uniqueTagList = new Dictionary<string, List<int>>();
foreach (var uniqueTag in uniquetags)
{
List<int> lineNumbers = new List<int>();
foreach (var item in data.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
//split data into tags
var tags = item.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var tag in tags)
{
if (uniqueTag == tag)
{
lineNumbers.Add(index);
}
}
}
//remove all having support threshold.
if (lineNumbers.Count > 5)
{
uniqueTagList.Add(uniqueTag, lineNumbers);
}
}

How to use nested for loops and recursion to create a list of combinations of strings

I have a SortedList in Key/Value pair that so far stores 3 entries like this:
Key: "Shapes" and Value: ["Cube", "Sphere"]
Key: "Colors" and Value: ["Red", "Green"]
Key: "Sizes" and Value: ["Big", "Small"]
My goal is generate all the combination of strings and store them into another list like this:
"Shape:Cube/Colors:Red/Size:Big"
"Shape:Cube/Colors:Red/Size:Small"
"Shape:Cube/Colors:Green/Size:Big"
"Shape:Cube/Colors:Green/Size:Small"
"Shape:Sphere/Colors:Red/Size:Big"
"Shape:Sphere/Colors:Red/Size:Small"
"Shape:Sphere/Colors:Green/Size:Big"
"Shape:Sphere/Colors:Green/Size:Small"
The caveat here is that there can be N number of entries in the first SortedList so I can't really create the for-loops in my source code before hand. I know I should use recursion to tackle the trickiness of the dynamic N value.
So far I've only come up with a hard-coded solution for N=2 entries and I'm having trouble translating into a recursion that can handle any value of N entries:
for (int ns=0; ns < listFeaturesSuperblock.Values[0].Count; ns++) {
for (int nc=0; nc < listFeaturesSuperblock.Values[1].Count; nc++) {
//prefab to load
string str = "PreFabs/Objects/" + listFeaturesSuperblock.Keys[0][ns] + ":" + listFeaturesSuperblock.Values[0][ns] + "/" + listFeaturesSuperblock.Values[1][nc] + ":" + listFeaturesSuperblock.Values[1][nc];
}
}
Can somebody kindly point me towards the right direction? How should I approach this and what do I need to study to get better at coding recursion?
Thank you.
In your current method:
List<string> result = new List<string>;
ProcessItems(listFeaturesSuperblock, result);
And this is the recursive method:
void ProcessItems(SortedList<string, List<string>> data, List<string> result, int level = 0, string prefix = "PreFabs/Objects/")
{
for (int i = 0; i < data.Values[level].Count; i++)
{
string item = prefix + data.Keys[level] + ":" + data.Values[level][i] + "/";
if (level == data.Values.Count - 1)
result.Add(item);
else
ProcessItems(data, result, level + 1, item);
}
}
The 'result' variable will then contain all permutations.
To use recursion is quiet a simple way and here's how.
Let's say we have Dictionary just like in your example
public static Dictionary<string, List<string>> props = new Dictionary<string, List<string>>(){
{ "Shapes", new List<string>{"Cube", "Sphere"} },
{ "Colors", new List<string>{"Red", "Green"} },
{ "Sizes", new List<string>{"Big", "Small"} }
};
Now we take all values of first key and go through them appending their values to the source string. So for the first value we will get
/Shapes:Cube
And now we do the same for the next key Colors, resulting
/Shapes:Cube/Colors:Red
We continue it while there are more unprocessed keys. When there are no more keys we got the first result string
/Shapes:Cube/Colors:Red/Sizes:Big
now we need to go back and add another value which result
/Shapes:Cube/Colors:Red/Sizes:Small
And the code for this will be like following
public static List<string> GetObjectPropertiesPermutations(string src, string[] keys, int index) {
if(index >= keys.Length) {
return new List<string>() { src };
}
var list = new List<string>();
var key = keys[index];
foreach(var val in props[key]) {
var other = GetObjectPropertiesPermutations(src + "/" + key + ":" + val, keys, index + 1);
list.AddRange(other);
}
return list;
}
public static void Main(string[] args)
{
var perms = GetObjectPropertiesPermutations("", props.Keys.ToArray(), 0);
foreach(var s in perms) {
Console.WriteLine(s);
}
}

Proper way to iterate this?

I have a list of strings that are semicolon separated.
There will always be an even number because the first is the key, the next is the value,
ex:
name;Milo;site;stackoverflow;
So I split them:
var strList = settings.Split(';').ToList();
But now I would like to use a foreach loop to put these into a List<ListItem>
I am wondering if it can be done via iteration, or if I have to use a value 'i' to get [i] and [i+1]
It can be done with LINQ but I am not sure this one is better
var dict = input.Split(';')
.Select((s, i) => new { s, i })
.GroupBy(x => x.i / 2)
.ToDictionary(x => x.First().s, x => x.Last().s);
You can also use moreLinq's Batch for this
var dict2 = input.Split(';')
.Batch(2)
.ToDictionary(x=>x.First(),x=>x.Last());
I can't compile this, but this should work for you:
var list = new List<ListItem>();
for (int i = 0; i < strList.Count; i++)
{
i++;
var li = new ListItem(strList[i - 1], strList[i]);
list.Add(li);
}
again, I'm not in a position to fully recreate your environment but since the first is the key and second is the value, and you're sure of the state of the string, it's a pretty easy algorithm.
However, leveraging a foreach loop would still require you to know a bit more about the index so it's a little more straight forward with a basic for loop.
First, a valuable helper function I use. It is similar to GroupBy except it groups by sequential indexes rather than some key.
public static IEnumerable<List<T>> GroupSequential<T>(this IEnumerable<T> source, int groupSize, bool includePartialGroups = true)
{
if (groupSize < 1)
throw new ArgumentOutOfRangeException("groupSize", groupSize, "Must have groupSize >= 1.");
var group = new List<T>(groupSize);
foreach (var item in source)
{
group.Add(item);
if (group.Count == groupSize)
{
yield return group;
group = new List<T>(groupSize);
}
}
if (group.Any() && (includePartialGroups || group.Count == groupSize))
yield return group;
}
Now you can simply do
var listItems = settings.Split(';')
.GroupSequential(2, false)
.Select(group => new ListItem { Key = group[0], Value = group[1] })
.ToList();
if you want to use foreach
string key=string.Empty;
string value=string.Empty;
bool isStartsWithKey=true;
var strList = settings.Split(';').ToList()
foreach(var item in strList)
{
if(isStartsWithKey)
{
key=item;
}
else
{
value=item;
//TODO: now you can use key and value
}
isStartsWithKey=!isStartsWithKey;
}
List<int, string> yourlist;
for(int i=0;i<strList.length/2;i++)
{
yourlist.add(new ListItem(strList[i*2], strList[i*2+1]));
}
this seems to me to be the simpliest way
for(var i = 0; i < strList.Count(); i = i + 2){
var li = new listItem (strList[i], strList[i + 1];
listToAdd.Add(li);
}
Updated Example
for (var i = 0; i < strList.Count(); i = i + 2){
if (strList.ContainsKey(i) && strList.ContainsKey(i + 1)){
listToAdd.Add(new listItem(strList[i], strList[i + 1]);
}
}

Categories

Resources