I'm learning to program in C#, and I'm running into a problem: I can't seem to call the elements that I've previously stored in a nested List, like so:
private List<List<float>> Order = new List<List<float>>();
private List<float> SubList = new List<float>();
private List<float> Conditions = new List<float>();
string tmp;
string[] levels;
string[] sublevels;
float curlevel;
tmp = "1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1"
levels = tmp.Split(';');
for(int i=0;i<levels.Length;i++){
sublevels=levels[i].Split(',');
for(int a=0;a<sublevels.Length;a++){
float.TryParse(sublevels[a], out curlevel);
SubList.Add(curlevel);
}
Order.Add(SubList);
SubList.Clear();
}
foreach(float cond in Order[3]){
Conditions.Add(cond);
}
Although printing the lists' counts shows that my Order list now has 6 items, and each SubList list has 3 items in it, I am not able to call the items in the sublists by indexing the Order list, and therefore my Conditions list, which I want to use in my project, stays empty. Is there another way to index items within a nested list? I also tried:
Conditions[0]=Order[3][0];
Conditions[1]=Order[3][1];
Conditions[2]=Order[3][2];
Which also didn't work. If anyone knows how to do this, I would be very grateful to have your input!
Change you code to
List<List<float>> Order = new List<List<float>>();
//List<float> SubList = new List<float>();
List<float> Conditions = new List<float>();
string tmp;
string[] levels;
string[] sublevels;
float curlevel;
tmp = "1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1";
levels = tmp.Split(';');
for (int i = 0; i < levels.Length; i++)
{
List<float> SubList = new List<float>();
sublevels = levels[i].Split(',');
for (int a = 0; a < sublevels.Length; a++)
{
float.TryParse(sublevels[a], out curlevel);
SubList.Add(curlevel);
}
Order.Add(SubList);
//SubList.Clear();
}
foreach (float cond in Order[3])
{
Conditions.Add(cond);
}
Your code should work of the keys really are numeric. I think you clear your sublist and lose your data with SubList.Clear(); Your first step is to put a breakpoint before and after you do this and Inspect your elements and what they look like.
Like the others write, it is your SubList.Clear() which removes all elements from SubList inside your Order List.
You can use
Order.Add(new List<float>(SubList));
This makes sures there is no reference from SubList.clear() and the list you just added to Order
You can just use the following linq query to achieve what you want:
var result = tmp.Split(';')
.Select(b => b.Split(',')
.Select(float.Parse)
.ToArray())
.ToList();
foreach (float cond in result[3])
{
Console.WriteLine(cond);
}
Related
I have this code which I create a list of id's with:
var listOfLists = new List<IEnumerable<string>>();
const int chunkSize = 999;
int consumed = 0;
while (consumed < IDList.Count())
{
listOfLists.Add(neueIDListe.Skip(consumed).Take(chunkSize).ToList());
consumed += chunkSize;
}
The problem is when I try to display the list all I get is:
System.Collections.Generic.List`1[System.String]
I already tried to display the value of the list with string.Join but this doesn't work either.
You are trying add data with list but .add is add data one by one. you shoult use foreach() or for() loop for add all data to your list or replace your code with this;
listOfLists = (neueIDListe.Skip(consumed).Take(chunkSize).ToList();
or if you want to append of your old list;
listOfLists += (neueIDListe.Skip(consumed).Take(chunkSize).ToList();
and you can try this for list definition but I dont know if it is works;
IEnumerable<string> listOfLists = new List<string>();
Use
var listOfLists = new List<string>();
instead of
var listOfLists = new List<IEnumerable<string>>();
I've been trying to figure out how to remove elements in my ArrayList where the value contains some text string.
My Array could look like this:
[0] "\"MAERSKA.CO\",N/A,N/A,N/A,N/A,N/A,N/A"
[1] "\"GEN.COABB.ST\",N/A,N/A,N/A,N/A,N/A,N/A"
[2] "\"ARCM.ST\",\"Arcam AB\",330.00,330.50,332.00,330.50,330.00"
And my ArrayList is created like this:
string stringToRemove = "NA";
ArrayList rows = new ArrayList(csvData.Replace("\r", "").Split('\n'));
So the question is how I delete all entries that contains "NA".
I have tried the RemoveAt or RemoveAll with several combinations of Contains but i cant seem to get the code right.
I do not want to make a new Array if it can be avoided.
Regards
Flemming
If you want to reduce your ArrayList before instantiate your variable, consider using LINQ:
ArrayList rows = new ArrayList(csvData.Replace("\r", "").Split('\n').Where(r => !r.Contains(stringToRemove)).ToList());
If you want to reduce your ArrayList after instantiation, you can try this:
for (int i = 0; i < rows.Count; i++)
{
var row = (string)rows[i];
if (row.Contains(stringToRemove))
{
rows.RemoveAt(i);
i--;
}
}
The following code creates a list as output containing all strings except "N/A":
var outputs = new List<string>();
foreach (var item in input)
{
var splitted = item.Split(',');
foreach (var splt in splitted)
{
if (splt != "N/A")
{
outputs.Add(splt);
}
}
}
The input is your array.
Trying to read a csv file, and take the first word in the stream, throw it in to a dictionary while the following words get added to a list in that dictionary.
However, I find that (during debugging) when, inside my loop I decide to clear my list, all of the values it had added to the dictionary previously also get cleared. I guess I am mistaken in assuming it makes a copy of the list, it is actually just referencing that same list? Should I be creating a new list with every iteration? Code below:
public class TestScript : MonoBehaviour {
// Use this for initialization
void Start() {
Dictionary<string, List<string>> theDatabase = new Dictionary<string, List<string>>();
string word;
string delimStr = ",.:";
char[] delimiter = delimStr.ToCharArray();
List<string> theList = new List<string>();
using (StreamReader reader = new StreamReader("testComma.csv")) {
while (true) {
//Begin reading lines
string line = reader.ReadLine();
if (line == null) {
break;
}
//Begin splitting lines, adding to array.
string[] split2 = line.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
//Loop to hold the first word in the stream
for(int i = 0; i <= 0; i++) {
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
theList.Clear();
}
}
}
foreach (KeyValuePair<string, List<string>> pair in theDatabase) {
string keys;
List<string> values;
keys = pair.Key;
values = pair.Value;
print(keys + " = " + values);
}
}
}
The bottom foreach loop is just so I can see the results. Also, any critique is welcome in regards to how this is written, as I'm a beginner.
Yes, you're adding the same object to your dictionary.
You can just change :
theDatabase.Add(word, theList);
To :
theDatabase.Add(word, theList.ToList());
Method ToList() makes shallow copy of your List<T>
C# is pass by reference.
So, theList and the list in your Dictionary are the same object.
The simplest solution is to stop clearing your List and create a new one every time instead:
for(int i = 0; i <= 0; i++) {
List<string> theList = new List<string>(); // it is in a loop now
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
//theList.Clear(); - not required anymore
}
It is more readable and clear solution: create a list, insert items, paste a list into the dictionary, continue the iteration.
It is also much more performant since there is no List clearing - List<T>.Clear() is a linear operation, which takes O(n) operations.
Yes, as everyone says, lists are reference types. You need to make a copy to avoid the .Clear() clearing all the lists.
You could always write your code like this:
void Start()
{
string delimStr = ",.:";
Dictionary<string, List<string>> theDatabase =
File
.ReadAllLines("testComma.csv")
.Select(line => line.Split(delimStr.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(x => x[0], x => x.Skip(1).ToList());
/* foreach here */
}
}
This doesn't have the problem with the list references.
I am getting "index out of range" from this loop. But I need to use new elements that loop founds, how do I do that? Please help to fix the problem
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
string[] links = new string[linkCount];
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
links[i] = linksToClick[i].GetAttribute("href");
}
I think that you could refactor your code:
var linkElements = driver.FindElements(By.CssSelector("a[href]")).ToList();
var links = new List<string>();
foreach (var elem in linkElements)
{
links.Add(elem.GetAttribute("href"));
}
If that works, you could simplify the query:
var instantLinks = driver.FindElements(By.CssSelector("a[href]"))
.Select(e => e.GetAttribute("href"))
.ToList();
You can rewrite your code to bypass the for loop:
string[] links = driver.FindElements(By.CssSelector("a[href]")).Select(l => l.GetAttribute("href")).ToArray();
This should also avoid the index out of range problem, and cut down the amount of code you have to write.
First of all i dont see a point in assigning linkstoclick values inside loop... And Reason for error must be that linksToClick list's length is more than that of linkCount.
int linkCount = driver.FindElements(By.CssSelector("a[href]")).Count;
List<string> links = new List<string>();
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
if (linksToClick.Count < i)
links.Add(linksToClick[i].GetAttribute("href"));
}
This might help with the out of range exception.
Doing this allows you to create a list of type: string without having to explicitly define the size of the list
the first one gets all of your elements by tag name ...let's assume 5.
in the loop, your driver get's all the elements by css selector, and you might have a different number here. let's say 4.
then, you might be trying to set the fifth element in a four element array.
boom.
Easiest fix to debug:
int linkCount = driver.FindElements(By.TagName("a")).Count;
string[] links = new string[linkCount];
// WRITE OUT HOM MANY links you have
for (int i = 0; i < linkCount; i++)
{
List<IWebElement> linksToClick = driver.FindElements(By.CssSelector("a[href]")).ToList();
// ASSERT THAT YOU HAVE THE SAME AMOUNT HERE
If (links.Count != linksToClick.Count)
// your logic here
links[i] = linksToClick[i].GetAttribute("href");
}
I have some strange problem where all my string arrays has the same value in the List.
Here is my code:
List<string[]> map_data = new List<string[]>();
string[] map_data_array = new string[11];
for(int i = 0; i < 2000; i++)
{
map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
map_data_array has always different data, I've verified that by placing the break point there and I've checked it.
The problem is that map_data has the value of all elements the same. And this value is the data that comes from function PopulateDataFromFile when the i is 1999.
What I am doing wrong? :/
That only happens if you place the same array into the list. As you did not give the code to PopulateDataFromFile we can only guess what happens. Make sure that the function returns a seperate array created with new each time.
You need to process your data in chunks since PopulateDataFromFile(); looks to be returning all of its data in one go (or as much as the array can fit). Using an extension method, you could do something like this: -
List<string[]> map_data = new List<string[]>();
foreach (var batch in PopulateDataFromFile().Batch(11))
{
map_data.Add((batch.ToArray());
}
Extension method: -
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int batchSize)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / batchSize)
.Select(g => g.Select(x => x.item));
}
PopulateDataFromFile() is returning a String array with the same values.
In the loop everytime you just change the address of map_data_array , so that's why always the value will get changed to the newer data obtained from the method call. Reinitialize the string array everytime will help. It should look something like this
for(int i = 0; i < 2000; i++)
{
string[] map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
or if its confusing for you can you make it simple by
for(int i = 0; i < 2000; i++)
{
map_data.Add(PopulateDataFromFile()); // store to List
}