So, I have a listbox with x number of items. On top of the listbox I have a TextBox (this is the search field). I try do develop an algorithm that removes items from the listbox, if it doesn't contain the searchword (variable keyword in the code). This is supposed to happen for each key the user types (on-the-fly). So, the code:
private void _keywordTextBox_TextChanged(object sender, EventArgs e)
{
string keyword = _keywordTextBox.Text;
if (keyword == searchtext || isSpace) // do nothing if space is typed - searchtext is a templatetext in the textbox ("type here to search...")
return; // ignore
else if (keyword == "")
{
listBox.Items.Clear();
foreach (string s in originalList)
listBox.Items.Add(s);
}
else
{
List<string> selection = new List<string>();
foreach (string s in originalList) // originalList is the listbox at startup
selection.Add(s);
listBox.BeginUpdate();
string[] keywordSplit = keyword.Split(' ');
try
{
for (int i = originalList.Count - 1; i >= 0; i--)
{
string[] selectionSplit = selection[i].Split(' ');
int l = 0; // number of hits
for (int j = 0; j < selectionSplit.Length; j++)
{
for (int k = 0; k < keywordSplit.Length; k++)
{
if (selectionSplit[j].ToLower().Contains(keywordSplit[k].ToLower()))
{
l++;
break;
}
}
}
if (l < keywordSplit.Length) // Not hit on all keywords
selection.RemoveAt(i);
}
}
finally
{
listBox.Items.Clear();
foreach (string s in selection) // Add selection in listbox
listBox.Items.Add(s);
if (listBox.Items.Count > 0)
listBox.SetSelected(0, true); // Select first item in listbox
listBox.EndUpdate();
}
}
}
The problem is hard to describe, other than it doesn't work as intended. The behavour is, as far as I can see, sporadic.
If I search for "ck flow", I should get a hit for stackoverflow. More importantly, it should also work if I deletes chars (delete key of backspace). Anybody?
Edit: more details:
The listbox should shrink and grow on each keystroke, based on what the user searches for. The listbox should keep every item that matches the keyword typed in by the user, and filter away that doesn't match.
Or you could try to work out a Regular Expression:
private void textBox1_TextChanged(object sender, EventArgs e)
{
string keyword = textBox1.Text;
if (string.IsNullOrEmpty(keyword.Trim()))
{
listBox1.Items.Clear();
listBox1.Items.AddRange(_originalList.ToArray());
}
else
{
Regex regex = new Regex(GetRegexPatternFromKeyword(keyword));
List<string> selection =
_originalList.Where(s => regex.IsMatch(s)).ToList();
listBox1.Items.Clear();
listBox1.Items.AddRange(selection.ToArray());
}
}
private static string GetRegexPatternFromKeyword(string keyword)
{
string[] words =
keyword.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(word => "(?=.*" + word.Replace(")", #"\)") + ")").ToArray();
return string.Join("", words);
}
disclaimer: there could be some cases where an input would 'destroy' the regex pattern
Your code increases l too often. For instance;
the text 'aaa aaa aaa' with searchword 'aaa bbb' will give an l of 3 because every time you find 'aaa' you increase l. So this will be a match even though 'bbb' is never found.
You can fix this (among others) by deleting found parts of keywordsplit and recreating keywordsplit anew before every search of a new selectionline.
l++;
break;
becomes
l++
keywordSplit.RemoveAt[k];
break;
and move
string[] keywordSplit = keyword.Split(' ');
to just before you start the k loop
Altough I feel there might be better ways to achieve what you want with a bit cleaner and faster code it should work.
I got it to work. #IvoTops helped me in the right direction. Just loop trough all the keyword first, and then the selections.
for (int j = 0; j < keywordSplit.Length; j++)
{
for (int k = 0; k < selectionSplit.Length; k++)
{
if (selectionSplit[k].ToLower().Contains(keywordSplit[j].ToLower()))
{
l++;
break;
}
}
}
Seems to work ok now.
Related
So basically I have a button that takes the strings delimited by line breaks in one text box that then formats them a particular way and puts them in a different text box. Everything looks fine when I run the code, however, when I copy and paste the text from the second textbox to a different place, it adds a line break after everything that I took from the original box.
private void ToTableButton_Click(object sender, EventArgs e)
{
StringBuilder tableText = new StringBuilder();
string[] lines = BasicTextBox.Text.Split('\n');
TableTextBox.Clear();
try
{
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append(lines[i]);
}
else
{
tableText.Append(lines[i] + " | ");
}
}
tableText.Append(Environment.NewLine);
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append("--");
}
else
{
tableText.Append("--|");
}
}
int currentPos = Convert.ToInt32(columnsUpDown.Value);
while (currentPos <= lines.Length)
{
tableText.Append(Environment.NewLine);
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append(lines[currentPos]);
}
else
{
tableText.Append(lines[currentPos] + " | ");
}
currentPos++;
}
}
}
catch
{
}
TableTextBox.Text = tableText.ToString();
}
I thought maybe this be because the split doesn't remove the \n but I wasn't sure how to remove it afterwards. Any advice would be greatly appreciated.
I would think that trimming the lines[currentPos] would be the correct way...
tableText.Append(lines[currentPos].ToString().Trim());
I want to make a clickable text through code in c# (its not a URL just ordinary text) so when i click that text it do linkclicked event.
It is for a dictionary so when I search something and in the description there is a word same from the storage I can click it to search that word.
for (int j = 0; j <= jml[i]; j++)
{
richTextBox4.AppendText(j + 1 + ". ");
string[] desk = sk[i, j].Split(' ');
for (int l = 0; l < desk.Count(); l++)
{
for (int m = 0; m < kata.Count(); m++)
{
if (desk[l] == kata[m])
{
richTextBox4.SelectionColor = Color.Blue;
desk[l] = LinkArea;
}
}
richTextBox4.AppendText(desk[l] + " ");
richTextBox4.SelectionColor = Color.Black;
}
richTextBox4.AppendText("\n");
}
my code for the moment(for the hyperlink type text just the color that work)
There is no built-in way but if you know what you want it is not hard to build.
Step one is to decide just how the clickable text is defined, read: What are the separators?
If you want single words it is rather easy. One way is using a regEx to identify words in a RTB. If you don't like the built-in way to define a word, I can post a code with user-defined separators.. But maybe you are happy with what have already.
If you need whole phrases (including spaces) to be clickable as one whole entity then you will have to be a little more inventive:
either you can add special characters around the phrases
or you can replace the spaces by non-breakable spaces
Or you could write a complete map into your text, but that probably defies your purpose.
Anything but simple words will mean that you will need to apply some changes to your text.
The result should be a function that can find the start and the end of the clickable text and return the clicked word/phrase.
Step Two is to look up the text in a Dictionary<string, string> and do what you want to do with the value, e.g. show the translation in a Label:
if (dictEnGE.ContainsKey(theClickedWord))
label.Text = dictEnGE[theClickedWord];
For this to work you must first prepare the dictionary by adding the key-value pairs: The keys are the clickable texts and the values are whatever you need to find:
Dictionary<string, string> dictEnGE = new Dictionary<string, string>();
dictEnGE.Add("house", "Haus");
dictEnGE.Add("man", "Mann");
dictEnGE.Add("mouse", "Maus");
//..
So the whole solution is: Prepare a dictionary, code the Mouseclick event to find the clicked text, look up the text in the dictioanry, do your thing with the value you find..
Update: Here is a function to get the clicked word:
string getWordAtIndex(RichTextBox RTB, int index)
{
string wordSeparators = " .,;-!?\r\n\"";
int cp0 = index;
int cp2 = RTB.Find(wordSeparators.ToCharArray(), index);
for (int c = index; c > 0; c--)
{ if (wordSeparators.Contains(RTB.Text[c])) { cp0 = c + 1; break; } }
int l = cp2 - cp0;
if (l > 0) return RTB.Text.Substring(cp0, l); else return "";
}
and here is how you could use it:
private void richTextBox1_MouseClick(object sender, MouseEventArgs e)
{
string word = getWordAtIndex(richTextBox1, richTextBox1.SelectionStart);
if (dictEnGE.ContainsKey(word)) aLabel.Text = dictEnGE[word];
}
I'm sure the first response will be 'wtf, don't use arraylists!', but I'm really just trying to get this working in any way that I can.
It's basically the 'match looker' for a match 3 game. I'm having trouble getting access to the match data within the match list. See below..
public void FindAndRemoveMatches() {
ArrayList foundMatches = LookForMatches();
//just checking if we're getting the right amount for now
Debug.Log("We found " + foundMatches.Count + " 'Match 3's");
foreach(Object el in foundMatches){
// Debug.Log(el.ToString());
}
}
ArrayList LookForMatches(){
//List<int> matchList = new List<int>();
ArrayList matchList = new ArrayList();
// search for horizontal matches
// note that we're subtracting two rows here.
// We don't need to check the last two rows because we're matching 3.
for (int i = 0; i < BOARD_WIDTH; i++){
for (int j = 0; j < BOARD_HEIGHT-2; j++){
ArrayList match = GetMatchHoriz(i,j);
if (match.Count > 2) {
matchList.Add(match);
i += match.Count-1;
}
}
}
// search for vertical matches
for (int i = 0; i < BOARD_WIDTH; i++){
for (int j = 0; j < BOARD_HEIGHT-2; j++){
ArrayList match = GetMatchVert(i,j);
if (match.Count > 2) {
matchList.Add(match);
j += match.Count-1;
}
}
}
return matchList;
}
// look for horizontal matches starting at this point
ArrayList GetMatchHoriz(int col,int row){
ArrayList match = new ArrayList();
match.Add(mBoard[col,row]);
for(int i = 1; (col+i)<8; i++) {
if (mBoard[col,row] == mBoard[col+i,row]) {
if(mBoard[col+i,row] > mPieces.GetNumPieceTypes()) match.Add(mBoard[col+i,row]);
} else {
return match;
}
}
return match;
}
// look for horizontal matches starting at this point
ArrayList GetMatchVert(int col,int row){
ArrayList match = new ArrayList();
match.Add(mBoard[col,row]);
for(int i = 1; (row+i)<8; i++) {
if (mBoard[col,row] == mBoard[col,row+i]) {
if(mBoard[col,row+i] > mPieces.GetNumPieceTypes()) match.Add(mBoard[col,row+i]);
} else {
return match;
}
}
return match;
}
Good news is, I know that it's finding the matches correctly. The number of matches it's finding using the debug log correlates with what's happening on the screen. Yay! But, I need to get access to that data so I can compare it to the board (mBoard[col,row]) and remove those objects from the game board.
The 'foreach' loop in findandremovematches gives an error about casting. Any help with this?
thanks!
I hope i understand your question correctly
foreach(Objecct obj in list)
{
ArrayList inner = obj as ArrayList;
if(inner != null)
{
//this is what you are looking for
//you can then iterate the inner array list and get the int[,] you saved
}
}
However, as suggested, use List<ArrayList> or List<List<int[,]>> if I guessed it right.
If you're using ArrayLists because you think they're easier, don't. Use List.
If you're using ArrayLists because you have to and there is no way for you to use List (which I doubt) then you'll have something like this:
foreach(ArrayList innerList in foundMatches)
{
foreach(SomeClassThatYouAddToInnerLists item in innerlist)
{
//use item;
}
}
Replace SomeClassThatYouAddToInnerLists with whatever the type is that you're adding to the inner ArrayLists. (The type of mBoard.)
If you use Lists then it's very clear what the type is of everything inside of the list. A List<List<int>> is easy to work with. It's a list of lists of numbers.
I am creating an application to search the user typed word from list box. I want to Show only that items in listbox which are matched with the character typed by the user. I am unable to find the exact syntax for this.
private void textBox1_TextChanged(object sender, EventArgs e)
{
string a=textBox1.Text;
for (int i = 0; i < listBox1.Items.Count; i++)
{
if(a[0]==listBox1.Items(i).char[0])//how to do this?
{........
}
}
}
if you want to check the char of a do something like this
also if you are not getting the "Text / String Value.. add the .ToString(); after listBox1.Items[i].ToString();
if(a[i]== listBox1.Items[i])
{
//i is the incremented value here..
}
foreach (char valchar in a)
{
// do your logic.. 'X' single quotes for Char
}
if you want to check for a string in a do
foreach (string valString in a)
{
// do your logic for a string check if valString = "X" for example "" double quotes for
}
Like this:
string a = textBox1.Text;
for (int i = 0; i < listBox1.Items.Count; i++)
{
if( a[0] == listBox1.Items[i].Text)
{
//Do Something...
}
}
I get a string from the user and then put it in a char array. Now I want to display all the characters in the string along with how many times they appear. My code is as follows Please Correct me ?
using System;
class count
{
public void charcount()
{
int i ;
int count = 0;
string s;
Console.WriteLine("Enter the String:");
s = Console.ReadLine();
char[] carr = s.ToCharArray();
for(i = 0; i < carr.Length; i++)
{
for(int j = 1; j < carr.Length; j++)
{
if(carr[j] == carr[i])
{
count++;
}
else
{
return;
}
Console.WriteLine("The Character " + carr[i] + " appears " + count);
}
}
}
static void Main()
{
count obj = new count();
obj.charcount();
}
}
Well, your code will at least have problems due to the fact that you don't build a list of unique characters, you find them in the original string. Any string with characters that appear multiple times will show odd results.
Here's a LINQ expression that calculates the information for you (you can run this in LINQPad to immediately see the results):
void Main()
{
string s = "This is a test, with multiple characters";
var statistics =
from c in s
group c by c into g
select new { g.Key, count = g.Count() };
var mostFrequestFirst =
from entry in statistics
orderby entry.count descending
select entry;
foreach (var entry in mostFrequestFirst)
{
Debug.WriteLine("{0}: {1}", entry.Key, entry.count);
}
}
Output:
: 6 <-- space
t: 5
i: 4
s: 4
h: 3
a: 3
e: 3
l: 2
c: 2
r: 2
T: 1
,: 1
w: 1
m: 1
u: 1
p: 1
If you can't use LINQ, here's an example that doesn't use that:
void Main()
{
string s = "This is a test, with multiple characters";
var occurances = new Dictionary<char, int>();
foreach (char c in s)
{
if (occurances.ContainsKey(c))
occurances[c] = occurances[c] + 1;
else
occurances[c] = 1;
}
foreach (var entry in occurances)
{
Debug.WriteLine("{0}: {1}", entry.Key, entry.Value);
}
}
It looks like you want an outer loop and an inner loop, and for each char in the array, you want to compare to each that follows with int j = 1. In that case, you want int j = i + 1 in the inner loop:
for (int i = 0; i < carr.Length; i++)
{
for (int j = i + 1; j < carr.Length; j++)
{
}
}
But your return statement exits the function right in the middle of things. You need to let the loops complete, so you want to remove that return.
Your Console.WriteLine executes on every iteration of the inner loop, but you really only want it to iterate on the outer loop -- once for each character of the string and not once for every combination of i and j. So you need to push that to the outer loop, outside of the inner loop.
Also, you need to reset the count every time you begin another iteration of the outer loop, because you are counting again, and, you want to start counting at 1 not zero when you find a character because it just appeared once as you first reach it.
And as Lasse points out, you'll get strange output when you hit the same character as you move along the outer loop. An easy way to prevent that is to set the further (rightwards) char[j] to '\0' (null character) on every match, and then in the outer loop, ignore null characters in your counting for example by using continue, effectively culling them as you go along:
for(int i = 0; i < carr.Length; i++)
{
if (carr[i] == '\0')
{
continue; // skip any further chars we've already set to null char
}
int count = 1;
for (int j = i + 1; j < carr.Length; j++)
{
if(carr[j] == carr[i])
{
carr[j] = '\0'; // don't look for this char again later
count++;
}
}
Console.WriteLine("The Character " + carr[i] + " appears " + count);
}
My first thought would be to use a Dictionary as Daniel suggests, like this:
var dict = new Dictionary<char, int>();
string s = "This is a test, with multiple characters";
foreach (var c in s)
{
if (dict.ContainsKey(c))
{
dict[c]++;
}
else
{
dict[c] = 1;
}
}
foreach (var k in dict.Keys)
{
Console.WriteLine("{0}: {1}", k, dict[k]);
}
But I like the elegant LINQ solutions.
If you would have tested your code, you would have realized, that the code is very wrong.
Some of its problems:
You have only one count variable, although you want to count the occurences of all characters in your string
You are comparing the string with itself and return from your method, as soon, as the characters don't match.
The right way would be to use a Dictionary<char, int> to hold the count for each character, something like this:
var counts = new Dictionary<char, int>();
foreach(var c in s)
{
int count;
if(!counts.TryGetValue(c, out count))
{
counts.Add(c, 1);
}
else
{
++counts[c];
}
}
I didn't use LINQ on purpose, because I don't think you would understand that code. That's no offence.
If you want to go the LINQ way, this is a fairly brief way to do it (which I realize is pretty much the same as Lasse V. Karlsen's answer, only using different syntax):
var s = Console.ReadLine();
foreach (var group in s.GroupBy(c => c).OrderByDescending(g => g.Count()))
{
Console.WriteLine(" {0}: {1}", group.Key, group.Count());
}
The logic is the same whatever approach you use:
Identify each unique character
Count how many times each character occurs in the text
Output the result
In my code sample, s.GroupBy(c => c) takes care of the first two steps. The call OrderByDescending(g => g.Count()) will just sort the result so that more frequent characters come first. Each element in the result has a Key property (the character) and (amongst others) a Count() method that will return the number of occurrences for that character.