This question already has answers here:
.net dictionary and lookup add / update
(6 answers)
Closed 2 years ago.
How can I append items to Dictionary using list inside?
Here is my code:
Dictionary<string, List<double>> listVariables = new Dictionary<string, List<double>>();
And this is the way that I'm appending new values:
for (int x = 1; x <= 4; x++) {
listVariables["foo" + x].Add(1.1);
}
No errors found, but when I start my application, it crashs and I got this:
An unhandled exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll
Additional information the given key was not present in the dictionary
I can make it works replaceing listVariables["foo" + x].Add(1.1); for listVariables["foo" + x] = new List<double> { 1.1 };
But this will always replace the first index value, I want append all data in a row of sequency
How can I solve that? Thank you very much.
You need to check if the key already exists, if not, you'll need to add it with an empty list:
for (int x = 1; x <= 4; x++)
{
var key = "foo" + x;
if (!listVariables.ContainsKey(key))
listVariables.Add(key, new List<double>());
listVariables[key].Add(1.1);
}
You need to check for the existence of the key first, adding a new list when necessary.
for (int x = 1; x <= 4; x++)
{
var key = "foo" + x;
if (!listVariables.ContainsKey(key))
listVariables[key] = new List<double>();
listVariables[key].Add(1.1);
}
Related
This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 2 years ago.
I'm getting the following error when executing selenium C# code in my test script
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
at System.Collections.Generic.List`1.get_Item(Int32 index)
What does this mean? What is the problem in my code?
// Access the project
IList<IWebElement> allRows = driver.FindElements(By.XPath("//*[#id='GridViewProjList_ctl00']/tbody/tr"));
IList<IWebElement> allPages = driver.FindElements(By.XPath("//div[#class='rgWrap rgNumPart']//a"));
string projectName = "TITAN";
for (int i = 0; i <= (allPages.Count); i++)
{
allRows = driver.FindElements(By.XPath("//*[#id='GridViewProjList_ctl00']/tbody/tr"));
for (int row = 1; row <= (allRows.Count); row++)
{
projectName = "TITAN";
IWebElement nameElement = driver.FindElement(By.XPath("//table/tbody/tr/td[2]/div/div/div/table/tbody/tr[3]/td/div/div/div/div[2]/table/tbody/tr[" + row + "]/td[1]"));
string name = nameElement.Text;
if (projectName.Contains(name))
{
nameElement = driver.FindElement(By.XPath("//table/tbody/tr/td[2]/div/div/div/table/tbody/tr[3]/td/div/div/div/div[2]/table/tbody/tr[" + row + "]/td[1]"));
nameElement.Click();
break;
}
allPages = driver.FindElements(By.XPath("//div[#class='rgWrap rgNumPart']//a"));
}
allPages = driver.FindElements(By.XPath("//div[#class='rgWrap rgNumPart']//a"));
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(5);
allPages.ToList()[i].Click();
Thread.Sleep(3000);
}
Thread.Sleep(3000);
Console.WriteLine($"{projectName} project has been successfully accessed");
Thread.Sleep(3000);
The thing is, your list size is not guaranteed to be the same during iteration:
The iterator is based on the allPages:
for (int i = 0; i <= (allPages.Count); i++)
Also note Guru Stron's comment here and correct the iterator bounds:
Also there is error in the length check, cause collections are zero-based it should be for (int i = 0; i < (allPages.Count); i++)
But the list is overridden multiple times, e.g.:
allPages = driver.FindElements(By.XPath("//div[#class='rgWrap rgNumPart']//a"));
And here you use the original indexer again:
allPages.ToList()[i].Click();
If the new list item count is less than i-1, you'll run into this error.
The solution is to not overwrite the list, or check the length when accessing the i-th element.
I am planning to get an array of the averages of each column.
But my app crashes at sum[j] += int.Parse(csvArray[i,j]); due to a FormatException. I have tried using Convert.ToDouble and Double.Parse but it still throws that exception.
The increments in the for loop start at 1 because Row 0 and Column 0 of the CSV array are strings (names and timestamps). For the divisor or total count of the fields that have values per column, I only count the fields that are not BLANK, hence the IF statement. I think I need help at handling the exception.
Below is the my existing for the method of getting the averages.
public void getColumnAverages(string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string[,] csvArray = csvToArray(filePath);
int[] count = new int[col];
int[] sum = new int[col];
double[] average = new double[col];
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
if (csvArray[i,j] != " ")
{
sum[j] += int.Parse(csvArray[i,j]);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach(double d in average)
{
System.Diagnostics.Debug.Write(d);
}
}
}
I have uploaded the CSV file that I use when I test the prototype. It has BLANK values on some columns. Was my existing IF statement unable to handle that case?
There are also entries like this 1.324556-e09due to the number of decimals I think. I guess I have to trim it in the csvToArray(filePath) method or are there other efficient ways? Thanks a million!
So there are a few problems with your code. The main reason for your format exception is that after looking at your CSV file your numbers are surrounded by quotes. Now I can't see from your code exactly how you convert your CSV file to an array but I'm guessing that you don't clear these out - I didn't when I first ran with your CSV and experienced the exact same error.
I then ran into an error because some of the values in your CSV are decimal, so the datatype int can't be used. I'm assuming that you still want the averages of these columns so in my slightly revised verion of your method I change the arrays used to be of type double.
AS #musefan suggested, I have also changed the check for empty places to use the IsNullOrWhiteSpace method.
Finally when you output your results you receive a NaN for the first value in the averages column, this is because when you don't take into account that you never populate the first position of your arrays so as not to process the string values. I'm unsure how you'd best like to correct this behaviour as I'm not sure of the intended purpose - this might be okay - so I've not made any changes to this for the moment, pop a mention in the comments if you want help on how to sort this!
So here is the updated method:
public static void getColumnAverages(string filePath)
{
// Differs from the current implementation, reads a file in as text and
// splits by a defined delim into an array
var filePaths = #"C:\test.csv";
var csvArray = File.ReadLines(filePaths).Select(x => x.Split(',')).ToArray();
// Differs from the current implementation
var col = csvArray[0].Length;
var row = csvArray.Length;
// Update variables to use doubles
double[] count = new double[col];
double[] sum = new double[col];
double[] average = new double[col];
Console.WriteLine("Started");
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
// Remove the quotes from your array
var current = csvArray[i][j].Replace("\"", "");
// Added the Method IsNullOrWhiteSpace
if (!string.IsNullOrWhiteSpace(current))
{
// Parse as double not int to account for dec. values
sum[j] += double.Parse(current);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach (double d in average)
{
System.Diagnostics.Debug.Write(d + "\n");
}
}
Dictionary<double, Tuple<int,int>> dictionary = new Dictionary<double, Tuple<int,int>>();
for (int i = 0; i < x.Length; i++)
for (int j = i+1; j < x.Length; j++)
{
double weight = Math.Round(Math.Sqrt(Math.Pow((x[i] - x[j]), 2) + Math.Pow((y[i] - y[j]), 2)), 2);
string edges = i + "-" + j + "-" + weight;
listBox1.Items.Add(edges);
Tuple<int, int> tuple = new Tuple<int, int>(i, j);
dictionary.Add(weight, tuple);
}
var list = dictionary.Keys.ToList();
list.Sort();
foreach (var key in list)
{
string a = dictionary[key].Item1 + "--" + dictionary[key].Item2 + "--> " + key.ToString();
listBox2.Items.Add(a);
}
I am trying to store some values in dictionary. But in the for loop suddenly its break with uncomplete values. There is no error message.
When i comment out "dictionary.Add(weight, tuple);" listbox is showing all data i want.
If you try to Add to a Dictionary a key which was already added, it'll throw a DuplicateKeyException. This is highly likely since you're rounding your double, resulting in several which will become the same value.
Assuming by the use of a ListBox that you're using this in a UI event (Forms, WPF, or otherwise) I would say it probably is throwing an exception, but something else is catching that exception and moving on.
When adding to a dictionary, you should check if the key already exists, and handle appropriately.
If you want to override the value, keep in mind that this[TKey key] will not throw an exception when adding a new item. Thus
// dictionary.Add(weight, tuple);
dictionary[weight] = tuple;
If you want to skip past a value that's already present, check for ContainsKey
if(!dictionary.ContainsKey(weight))
dictionary.Add(weight, tuple);
i'm trying to get X number of random number (where X is a variable) between 0 and 100 and add them to a row in a DataGridView. I'm using the code below, but the problem is i need it to be impossible to have the same number twice. Is there a way make sure i get unique random numbers?
int X = 20;
Random random = new Random();
int randomrow = random.Next(0, 100);
for (int i = 0; i < X; i++)
{
int randomNumber = random.Next(0, 100);
data.Rows[randomrow][3] = randomNumber;
}
Thanks for the help!
Split your problem in two:
Create a list of X random, unique numbers. There are numerous ways to do that:
a. Create a list of all numbers 0-100 and then shuffle them. OR
b. Keep a hash set of all the numbers you already created (in addition to the list) and only add a new one if it has not been added before.
Afterwards, loop through the list and the data rows simultaneously and insert the values into the rows.
Here's the simple way to do it without creating a shuffle method for the List (though there are examples of that). Basically create a list of all possible integers, populate the list, then sort by new GUIDs.
int X = 20;
var RowNumbers = new List<int>();
for (int i = 0; i < X; i++)
RowNumbers.Add(i);
foreach (int i in RowNumbers.OrderBy(f => Guid.NewGuid()))
{
data.Rows[i][3] = i;
}
You would need to compare the numbers you have already used to the next one you get from random.Next. If you have already used it, pick another. I also like Heinzi's answer.
Here is algorithm without shuffling and previous result using:
var max = 100; // max available value
var count = 20; // number of random numbers
var random = new Random();
var rest = (double)max;
for (int i = 0; i < max; i++, rest--)
{
if (count / rest > random.NextDouble())
{
Console.WriteLine(i); // here is your random value
count--;
if (count == 0)
{
break;
}
}
}
This question already has answers here:
HashSet with Index Access
(5 answers)
Closed 9 years ago.
I can't use a HashSet because I still need enumeration. If you're wondering why I need this, this is why:
private List<Point> sanitize(List<Point> crossPoints) {
HashSet<int> indexesToDelete = new HashSet<int>();
for (int i = 0; i < crossPoints.Count(); i++) {
if ((crossPoints[i].X - crossPoints[i + 1].X) <= 4) {
indexesToDelete.Add(i);
indexesToDelete.Add(i + 1);
}
}
for (int i = 0; i < crossPoints.Count(); i++) {
if ((crossPoints[i].Y - crossPoints[i + 1].Y) <= 4) {
indexesToDelete.Add(i);
indexesToDelete.Add(i + 1);
}
}
for (int i = 0; i < indexesToDelete.Count; i++) {
crossPoints.RemoveAt(indexesToDelete[i]);
}
return crossPoints;
}
This won't compile because of indexesToDelete[i]
If you want to iterate through the HashSet you can do:
foreach(int i in indexesToDelete)
{
...
But be careful when you delete items with RemoveAt! If you are not deleting the last entry, some of the other items will be moved and therefore get a new index.
One way to do this would be to sort the indexes and remove items from the highest to the lowest index.
var indexes = indexesToDelete.OrderByDescending(x => x).ToList();
foreach (int i in indexes)
{
crossPoints.RemoveAt(i);
}
You could also use SortedSet instead of HashSet.
SortedSet<int> indexesToDelete = new SortedSet<int>();
...
foreach(int i in indexesToDelete.Reverse())
{
crossPoints.RemoveAt(i);
}
There's an OrderedDictionary Class; however, it is not generic. The SortedDictionary<TKey, TValue> Class is generic. Note the difference between orderd and sorted. Ordered means that your insert order is kept intact. Sorted means that the insert order is ignored in favor of a sorting order.