I working on a project in c# asp.net. I would like to randomly display a string from either an array or list, if certain conditions are met, I would like to display another random string that has not been displayed.
EX.
LIST<string> myString = new List<string> {"one", "two", "three", "four", "five"};
or
string[] myString = new[] {"one", "two", "three", "four", "five"};
void btnAnswer_Click(Object sender, EventArgs e)
{
string Next = myString[random.Next(myString.Length)];
if(my condition is met)
{
lbl.Text = Next;
}
}
I can randomly call a string from my list or array, but not sure how to store repeat results. I've tried using an algorithm that jumbles the array and then a counter for the index, but the counter (counter++) seems to add 1 once and then stops. I've tried using a list and removing the string I use, but it seems to repeat after all strings have been used.
If more code is needed I can provide, just need a point in the right direction.
One option is to display one of the random string and store it in a ViewState.
When you come next time again, check whether new random string is already there in ViewState or not. If it is there in ViewState then get another random string.
Couple of options:
Have an array or list which is a copy of the master array, and whenever you choose one, remove it from the array. Once the array is empty, refresh it from the master array.
Similar, just create your array, shuffle it, and start giving out strings by order. so array[0], next would be array[1], and so on, once you reach the end, shuffle and start again.
There are probably others as well :)
Edit:
If I understand correctly from the comments, you are talking about the option where the input is not unique to begin with. If that is the case, you can create your list with a simple linq query to get only unique values (using distinct), guaranteeing no duplicates.
Have a look at this question for an example.
You can copy the indices into a list, it will save memory while allow us to track all the remaining items:
var indices = Enumerable.Range(0,myString.Count).ToList();
//define some method to get next index
public int? NextIndex(){
if(indices.Count == 0) return null;
int i = random.Next(indices.Count);
int k = indices[i];
indices.RemoveAt(i);
return k;
}
if(my condition is met) {
int? nextIndex = NextIndex();
lbl.Text = nextIndex == null ? "" : myString[nextIndex.Value];
}
Note that the Text is set to empty if there won't be no more remaining string, however you can handle that case yourself in another way such as keep the text unchanged.
You could probably do that, but why not get a unique list of strings first?
var uniqueStrings = myString.Distinct().ToList();
Then as you select strings, do a .Remove() on the last randomly selected value from uniqueStrings.
You said that:
I've tried using a list and removing the string I use, but it seems to repeat after all strings have been used.
The problem here is using the same instance of random for your series after you've run out. If you re-instantiate random = new Random(), the variable is re-seeded and you will have totally different results from what was generated before.
Related
I initiated an empty array - line.
string[] line = new string[] { };
I want to store every line that would be outputed in a cmd processing with the while loop below. This seems to work easily if I store the values in a string variable.
As shown below:
while (!proc.StandardOutput.EndOfStream)
{
line = proc.StandardOutput.ReadLine();
}
However, I'm not sure how to store the values as separate elements in the array. I've tried:
while (!proc.StandardOutput.EndOfStream)
{
for(a in line)
{
a = proc.StandardOutput.ReadLine();
}
}
But its not working.
This is probably a very basic question. But I'm still learning C#.
There are few solutions. One would be to use List<string> instead of string[]:
List<string> line = new List<string>();
And than add lines next way:
while (!proc.StandardOutput.EndOfStream)
{
line.Add(proc.StandardOutput.ReadLine());
}
An array works on the basis of indexing. So if you want to use an array you need to specify how long it has to be or in other words how many items it can contain:
// this array can store 100 items
string[] line = new string[100];
To access a certain position you need to use the [ ] operator and to move forward in the array you need an indexing variable of type int that you can increment each iteration
int indexer = 0;
while (!proc.StandardOutput.EndOfStream)
{
line[indexer] = proc.StandardOutput.ReadLine();
indexer ++; // increment
}
This way you need to know in advance how many items you want to deposit in your array.
Another way would be to use a flexible collection like List which can dynamically grow. Sidenote: The indexing works with the same [ ] operator, but the adding of items works via the Add method
If you want to know more have look at this overview of possible collection types
Here is what I have done:
// this is an example of my function, the string and the remover should be variables
string delimeter = ",";
string remover="4";
string[] separator = new string[] { "," };
List<String> List = "1,2,3,4,5,6".Split(separator, StringSplitOptions.None).ToList();
for (int i = 0; i < List.Count - 1; i++)
{
if(List[i]==remover)
List.RemoveAt(i);
}
string allStrings = (List.Aggregate((i, j) => i + delimeter + j));
return allStrings;
The problem is the retruned string is the same as the originial one, same as "1,2,3,4,5,6". the "4" is still in it.
How to fix it?
EDIT:
the solution was that i didnt check the last node of the list in that for, it doesnt seem like that in the example because it was an example i gave just now
When you remove items from a list like this you should make your for loop run in reverse, from highest index to lowest.
If you go from lowest to highest you will end up shifting items down as they are removed and this will skip items. Running it in reverse does not have this issue.
Your code, as it stands, produces the expected output. When running it it will return 1,2,3,5,6. If it doesn't it's due to a bug in how you call this method.
That's not to say that you don't have problems.
When you remove an item you still increment the current index, so you skip checking the item after any item you remove.
While there are a number of solutions, the best solution here is to use the RemoveAll method of List. Not only does it ensure that all items are evaluated, but it can do so way more efficiently. Removing an item from a list means shifting all of the items over by one. RemoveAll can do all of that shifting at the end, which is way more efficient if a lot of items are removed.
Another bug that you have is that your for loop doesn't check the last item at all, ever.
On a side note, you shouldn't use Aggregate to join a bunch of strings together given a delimiter. It's extremely inefficient as you need to copy all of the data from the first item into an intermediate string when adding the second, then both of those to a new string when adding the third, then all three of those to a new string when creating a fourth, and so on. Instead you should use string.Join(delimeter, List);, which is not only way more efficient, but is way easier to write and semantically represents exactly what you're trying to do. Win win win.
We can now re-write the method as:
string delimeter = ",";
string remover = "4";
List<String> List = "1,2,3,4,5,6"
.Split(new[] { delimeter }, StringSplitOptions.None).ToList();
List.RemoveAll(n => n == remover);
return string.Join(delimeter, List);
Another option is to avoid creating a list just to remove items from it and then aggregate the data again. We can instead just take the sequence of items that we have, pull out only the items that we want to keep, rather than removing the items we don't want to keep, and then aggregate those. This is functionally the same, but remove the needless effort of building up a list and removing items, pulling out mechanism from the requirements:
string delimeter = ",";
string remover = "4";
var items = "1,2,3,4,5,6"
.Split(new[] { delimeter }, StringSplitOptions.None)
.Where(n => n != remover);
return string.Join(delimeter, items);
Use this for remove
list.RemoveAll(f => f==remover);
So I have programmed for about 6 months now and since it is break I am bored and playing around with stuff. I was going over the concepts of arrays again and made an array that produced 50 randoms numbers and used an insertion sort on them to put them in order. Then I started messing with string arrays. string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I"}; is my string array. To get it to display the entire string I used a linked list (which I am not sure if I had to, my attempts to casting it didn't work i.e Convert.ToString(array) failed). so I have this
static void Main(string[] args)
{
string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I"};
List<string> list = new List<string>(array);
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
}
This displays the entire array. Then I thought what if I want to remove or add something to it. So, I placed list.RemoveAt(2); above my Console.Writeline(list[i]);
But this did something I didn't expect. It removes everything at and after the 2nd spot in my list. So I assumed RemoveAt() is going to always remove everything after that value that is indicated.
However, list.Remove(2); gives my errors. 1. Argument 1: cannot convert from 'int' to 'string' and then 2. The best overloaded method match...etc.. argument.
Again my casting didn't work. So, I reverted to RemoveAt and thought for a little bit. For some reason I thought if I make an if statement with an equivalent it would work, and it did.
static void Main(string[] args)
{
string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I" };
List<string> list = new List<string>(array);
for (int i = 0; i < list.Count; i++)
{
if (i == 2)
{
list.RemoveAt(2);
}
Console.WriteLine(list[i]);
}
So, my questions are these:
Why do the exceptions take place at Remove() but not RemoveAt().
Why does this if statement work? I took a shot in the dark at it and it works, I can understand why it works, but then not why a simply RemoveAt() wouldn't work.
Last, is there a better way to convert than a linked list?
Thank you for anyone who answers these question. I am new to programming and I am trying to understand some concepts that flew past me in my book and on MSDN.com and DotPearl...
RemoveAt only removes the element at that index. It does not remove all the elements following it.
Unless.... you place it in a loop.
Which you did.
So your loop did this:
Remove element 2, output element 0
Remove element 2, output element 1
Remove element 2, output element 2
... and so on
This "works" because of this:
for (.....; i < list.Count; ..... )
^-- this ----^
Basically, your loop and list looked like this:
List is "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I" (4 elements, 0-based, elements 0 through 3)
Index is 0, Remove element 2, "Baldur's Gate", Output element 0, "Ultima Online"
Index is 1, still below length (which is now 3), so remove element 2, "Diablo I", output element 1, "Everquest"
Index is 2, which is now equal to length (which is now 2), so exit
You changed the list while enumerating over it, and you changed it every iteration.
The reason why list.Remove(2) doesn't work is because of this:
list.RemoveAt(x) specifies an index, and the element at that index is removed
list.Remove(x) specifies a value, and the first element in the list that has that value will be removed
Now in this case you have said the list is a list of strings, and you ask it to remove an integer. C#/.NET doesn't allow such shenanigans, and thus told you about the problem before even allowing you to run the program.
For more information, check out the documentation of:
List<T>
List<T>.RemoveAt method
List<T>.Remove method
Remove takes a parameter of type T which corresponds to the object you want to remove. In your instance, Remove takes a string parameter which is supposed to correspond with one of the string items in your list (e.g. Ultima Online).
RemoveAt takes an int which corresponds to the index of the item in the list you want to remove. Once removed, everything is "shifted", so that what was at index 3 is now at index 2. When your loop passes over the RemoveAt(2) again, it removes the item that is now at index 2.
Why do the exceptions take place at Remove() but not RemoveAt()?
You get the exception Argument 1: cannot convert from 'int' to 'string' because the method signature is List<T>.Remove<T>(T). It expects a string.. and you're giving it a number.
Why does this if statement work?
Because you're completely free to compare numbers with numbers. Again, you've used RemoveAt here.. which expects a number.. and you've correctly provided one.
Also, a List<T> isn't a linked list. It is in fact a list backed by an array. An important distinction imo.
RemoveAt works by index. Remove is using the types definition of equality. You have a List<string>, the type is string, string does character by character comparison to determine equality, to remove a string you need to do;
list.Remove("Diablo I");
The issue with your RemoveAt getting rid of everything but the first item is well explained by Dave Zych's answer so I won't go into it.
I will talk about you converting to a list and your use of the term "Linked List" though. So firstly, List<T> in .NET is in no way a linked list. It behaved like one in some ways, however it is backed by an array. There is no need for you to convert to it. To print your array you could do either of the following;
for (int i = 0; i < array.Length; i++)
Console.WriteLine(array[i]);
or
Console.WriteLine(String.Join("\n", array));
You could easily "remove" an item by doing;
array[i] = null;
Assuming you haven't done stuff like string temp = array[i] elsewhere before that line you would be setting the final existing reference to that string to null which basically puts in queue to be GC'd.
I'm trying to implement a previous and next buttons.
I have a list of string called list1 which is filled with whatever is a user has inputted from a textbox. I can't access the previous (not the last) string in the list. IndexOf method isn't useful as I don't know what user will input.
private void previousBtn_click(object sender, EventArgs e)
{
getList();
int min = 0;
int max = list1.Count;
if(max==min)
{
previousBtn.Visible = false;
}
else
{
int temp =list.Count-1;
//how do I get my string if I know the element index from the previous line?
//textbox1.Text = thatPreviousString;
}
}
Sorry, it should be easy but I can't figure it out. How can I actully get my previous string in the list if the value is kind of unknown to me, so I can't just use find() and indexOf.
MSDN shows that there is a property called Item but there is no proper tutorial or code bit that shows how to use it.
UPDATE:
Let's say the user has typed "www.google.com", "www.facebook.com", "twitter.com" and then "www.yahoo.com". This urls are saved in list1. The last one was "www.yahoo.com", I can get it by calling Last(). The user can press the previous button anytime, so I can't specify the number of elements in list1, it's growing dynamically. I can only get the number of elements by calling
list1.Count and the last index by calling list1[list1.Count-1]
Now I know the number of indexes and elements, so how do I get the previous string, e.g. "www.twitter.com", if I can only say I can give you the index, give my string back?
By the way, ElementAs is only for arrays, doesn't work for lists.
how do I get my string if I know the element index from the previous line?
int prevIndex; // element index from the previous line that you know
string temp = list1[prevIndex - 1];
string temp =list[list.Count-1]; will give you the last element in the list.
string temp =list[list.Count-2]; will give you the previous element.
Remember, lists are 0-indexed, ie the first element is accessed via [0], so the last element will be [list size - 1].
So in your case textbox1.Text = list[list.Count-2]; will write the previous string into the textbox.
However, that won't give you a proper previous functionality. Pressing previous again won't give you list[list.Count-3]. You could though have a currentIndex variable that you decrement whenever previous is pressed for example and do textbox1.Text = list[currentIndex].
I was wondering if there is a way in an ArrayList that I can search to see if the record contains a certain characters, If so then grab the whole entire sentence and put in into a string. For Example:
list[0] = "C:\Test3\One_Title_Here.pdf";
list[1] = "D:\Two_Here.pdf";
list[2] = "C:\Test\Hmmm_Joke.pdf";
list[3] = "C:\Test2\Testing.pdf";
Looking for: "Hmmm_Joke.pdf"
Want to get: "C:\Test\Hmmm_Joke.pdf" and put it in the Remove()
protected void RemoveOther(ArrayList list, string Field)
{
string removeStr;
-- Put code in here to search for part of a string which is Field --
-- Grab that string here and put it into a new variable --
list.Contains();
list.Remove(removeStr);
}
Hope this makes sense. Thanks.
Loop through each string in the array list and if the string does not contain the search term then add it to new list, like this:
string searchString = "Hmmm_Joke.pdf";
ArrayList newList = new ArrayList();
foreach(string item in list)
{
if(!item.ToLower().Contains(searchString.ToLower()))
{
newList.Add(item);
}
}
Now you can work with the new list that has excluded any matches of the search string value.
Note: Made string be lowercase for comparison to avoid casing issues.
In order to remove a value from your ArrayList you'll need to loop through the values and check each one to see if it contains the desired value. Keep track of that index, or indexes if there are many.
Then after you have found all of the values you wish to remove, you can call ArrayList.RemoveAt to remove the values you want. If you are removing multiple values, start with the largest index and then process the smaller indexes, otherwise, the indexes will be off if you remove the smallest first.
This will do the job without raising an InvalidOperationException:
string searchString = "Hmmm_Joke.pdf";
foreach (string item in list.ToArray())
{
if (item.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0)
{
list.Remove(item);
}
}
I also made it case insensitive.
Good luck with your task.
I would rather use LINQ to solve this. Since IEnumerables are immutable, we should first get what we want removed and then, remove it.
var toDelete = Array.FindAll(list.ToArray(), s =>
s.ToString().IndexOf("Hmmm_Joke.pdf", StringComparison.OrdinalIgnoreCase) >= 0
).ToList();
toDelete.ForEach(item => list.Remove(item));
Of course, use a variable where is hardcoded.
I would also recommend read this question: Case insensitive 'Contains(string)'
It discuss the proper way to work with characters, since convert to Upper case/Lower case since it costs a lot of performance and may result in unexpected behaviours when dealing with file names like: 文書.pdf