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.
Related
I have a class Plott. One of the attributes is a list with header names called Headers. The list stores the names of all the columns within the plot.
I need to loop through these objects (plt which are in a List) and find any occurrences of headers containing the string "aaa" and replace it with the string "bbb".
I successfully loop through and find these headers within these objects. I can also define a new string which replaces "aaa" with "bbb" but when I try to assignee the new string i.e that index of the List to that object it gives an error regarding that the set has changed and the loop can't continue (the error message is not in English so I wont post it here)
foreach (Plott obj in plt) {
int c = 0;
foreach (String s in obj.Headers)
{
if (s.Contains("aaa"))
{
string newstr;
newstr = s.Replace("aaa", "bbb");
obj.Headers[c] = newstr;
}
c++;
}
}
Just replace the whole list.
obj.Headers = obj.Headers.Select( x => x.Replace("aaa","bbb") ).ToList();
The variable you retrieve in a foreach loop is a readonly copy of the element in the collection, so you can't modify it.
If you want to do this then you need to use a simple for loop
for (int ind = 0; ind < plt.Count(); ind++)
{
....
}
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'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);
}
Im trying to sort a listbox full of numbers numerically. Why doesnt this work?
{
ArrayList Sorting = new ArrayList();
Sorting.Add (lbNumbers.Text);
int[] items = new int[Sorting.Count];
Sorting.CopyTo(items);
Array.Sort(items);
lbNumbers.Items.Add(items);
}
Probably because when your numbers are represented as strings, they will not sort the way you expect. They will sort as strings and not as numbers.
For example, if you had a list such as:
10
9
101
It would be sorted as:
10
101
9
First, parse the string-elements, then sort.
// the itemList is your lbNumbers.Text
var itemList = new List<string> {"9", "1", "10", "11"};
// use TryParse if you're not sure if really all elements are numbers
var numberList = itemList.Select(int.Parse).ToList();
numberList.Sort();
ArrayList Sorting = new ArrayList();
foreach (var o in listBox1.Items) {
Sorting.Add(o);
}
Sorting.Sort();
listBox1.Items.Clear();
foreach (var o in Sorting) {
listBox1.Items.Add(o);
}
ADDED: For sort in descending order,
1.Create a class ReverseSort as shown below:
// implementation:
public class ReverseSort : IComparer
{
public int Compare(object x, object y)
{
// reverse the arguments
return Comparer.Default.Compare(y, x);
}
}
2.Replace the code line of Sort with this line:
Sorting.Sort(new ReverseSort());
You are sorting lbNumbers.Text => strings
You must clear before sorting
ArrayList arrayList = new ArrayList();
foreach (object o in lbNumbers.Items)
{
arrayList.Add(o);
}
arrayList.Sort();
lbNumbers.Items.Clear();
foreach(object o in arrayList)
{
lbNumbers.Items.Add(o);
}
Using a little bit of LINQ
string list = "1,24,3,10,12,11";
//Split the string into the tokens containing the numbers
string[] tokens = list.Split(',');
//Parse each string representing an integer into an integer
//return the resultant object as an array of integers
int[] sorting = tokens.Select(x => int.Parse(x)).ToArray<int>();
//Sort them numerically and return as an array of integers
sorting = sorting.OrderBy(x => x).ToArray<int>();
//Display them to convince ourselves it works.
foreach (int x in sorting)
{
Console.WriteLine(x);
}
Console.ReadLine();
The parsing and ordering can be done in the same statement, but were split out here for ease of reading.
Try this example:
listBox1.Items.Add(3);
listBox1.Items.Add(1);
listBox1.Items.Add(2);
ArrayList sort = new ArrayList();
foreach (object item in listBox1.Items)
{
sort.Add(item);
}
sort.Sort();
listBox1.Items.Clear();
foreach (int item in sort)
{
listBox1.Items.Add(item);
}
What you where trying to do was to read only the selected text. This way you can get all items in the listbox to an arraylist, sort them and then adding them back again to the listbox.
Keep in mind that all the unsorted items are still there so you need to clear the listbox first. Thats what the listBox1.Items.Clear(); does
I am trying to make a 1d array of lists. I make it like this:
public static List<string>[] words = new List<string>[30];
public static List<string>[] hints = new List<string>[30];
And I call it like this:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}
I just wanted to add that vars is where I keep my public variables and that counter is indeed at 0 when the loop starts.
EDIT
In my haste I forgot to add the question... oops...
Here it is: When I call the add function (or any another function for that matter) it returns a null reference exception. How can I fix this?
I assume you're crashing when attempting to call .Add on your array element. You need to initialize your arrays with valid objects.
for( Int32 i = 0; i < vars.words.Length; ++i )
vars.words[i] = new List<string>();
for( Int32 i = 0; i < vars.hints.Length; ++i )
vars.hints[i] = new List<string>();
Why not just make a List<List<string>>, but yes you can make an array of lists
Using a list of lists, as already recommended, would make you escape your problems,
and it´s much more flexible and handy than your construction.
-> f.i. if the size of your data changes, you don´t have to change the list size, but the array
Here's a one-liner to initialize an array of lists of size 30:
static List<string>[] lists = (from i in Enumerable.Range(0, 30)
select new List<string>()).ToArray();
The problem is that array values are initialized to the default value, and the default value for reference types is null.
default(List<string>) returns null.
So, you'll need to re-initialize the objects in the array before you can access them, otherwise you will get a NullReferenceException.
One way to initialize all the objects in your array up front is to use this Linq statement:
const int sizeOfLists = 5;
List<string>[] lists = Enumerable.Range(0, sizeOfLists)
.Select(i => new List<string>())
.ToArray();
Another option is to initialize and add the sub-lists only when you need them, by using an outer List:
var lists = new List<List<string>>();
// ...
var aSubList = new List<string>();
lists.Add(aSubList);
This is particularly useful if you don't know the size of the outer set of lists up-front, and is still accessible by index.
(This was a comment before, but I made it an answer since many other answers got caught up in the solution and don't describe the problem)
You could initialize the lists right before you use them:
foreach (string item in vars.directory)
{
reader2 = new StreamReader(item);
while (reader2.Peek() > 0)
{
string line = reader2.ReadLine();
// new code
if (vars.words[counter] == null) vars.words[counter] = new List<string>();
if (vars.hints[counter] == null) vars.hints[counter] = new List<string>();
if (line.StartsWith("#"))
{
vars.words[counter].Add(line.Substring(1, line.Length - 1)); //here
}
else if (line.StartsWith("-"))
{
vars.hints[counter].Add(line.Substring(1, line.Length - 1)); //another here
}
else if (line == "#end")
{
counter++;
}
}
}