Comparing char arrays method - c#

I've been playing space engineers which has been epic since they added in-game programming, I'm trying to make a gps auto-pilot navigation script and have to get the block positions finding the blocks by name looking for a smaller string within their bigger string name. I wrote this method to find a small string (word) in a larger string (name of the block):
bool contains(string text, string wordInText)
{
char[] chText = text.ToCharArray();
char[] chWord = wordInText.ToCharArray();
int index = 0;
for(int i = 0 ; i < chText.Length - chWord.Length ; i++)
for(int j = 0; j < chWord.Length;j++,index++)
if (chWord[0] == chText[i])
index = i;
else if (chWord[j] == chText[index]){}
else if (index == chWord.Length-1)
return true;
else break;
return false;
}
Am I even doing it right, should I be doing it another shorter way?

This is simple with .Contains() which returns a bool.
text.Contains(wordInText);

If you simply want to check if a string contains an other string, then you can use string.Contains, the string class already provides a bunch of methods for string operations.

As already mentioned, the String class already has a Contains method that should give you what you need.
That said, your code doesn't work. I can see where you're going with it, but it's just not going to work. Stepping through it in a proper dev environment would show you why, but since this is more in the way of a script that's probably not an option.
So... the basic idea is to iterate through the string you're searching in, looking for matches against the string your searching. Your outer for statement looks fine for this, but your inner statements are a bit messed up.
Firstly, you're doing the first character check repeatedly. It's wasteful, and misplaced. Do it once per iteration of the outer loop.
Second, your exit condition is going to fire when the first character of wordInText matches the characters at index wordInText.Length in text which is not apparently what you want.
In fact you're all tripped up over the index variable. It isn't actually useful, so I'd drop it completely.
Here's a similar piece of code that should work. It is still much slower than the library String.Compare method, but hopefully it shows you how you might achieve the same thing.
for (int i = 0; i <= chText.Length - chWord.Length; i++)
{
if (chText[i] == chWord[0])
{
int j;
for (j = 0; j < chWord.Length; j++)
{
if (chText[i + j] != chWord[j])
break;
}
if (j == chWord.Length)
return true;
}
}
return false;

Related

How to make this code more functional or 'prettier'

I've been working on a project where I need on a button press that this line gets executed.
if (listView1.SelectedItems[0].SubItems[3].Text == "0") //Checks to see Value
{
listView1.SelectedItems[0].SubItems[3].Text = "1";// If Value is Greater, Increase and Change ListView
questionNumberLabel.Text = listView1.SelectedItems[0].SubItems[3].Text;// Increase and Change Label
}
Now I have this repeated about 10 times with each value increasing by one. But I know that this is ugly, and dysfunctional. As well as conflates the file size. I've tried a few things. Primarily this method.
if (listView1.SelectedItems[0].SubItems[3].Text == "0")
{
for (var i = 1; i < 100;)
{
if (!Int32.TryParse(listView1.SelectedItems[0].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[0].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
}
But instead of just adding one, it does the 100 instances and ends. The reason this is becoming a pain in the *** is because the
listView1.SelectedItems[0].SubItems[3].Text
is just that - it's a string, not an int. That's why I parsed it and tried to run it like that. But it still isn't having the out come I want.
I've also tried this
string listViewItemToChange = listView1.SelectedItems[0].SubItems[3].Text;
Then parsing the string, to make it prettier. It worked like it did before, but still hasn't given me the outcome I want. Which to reiterate is, I'm wanting the String taken from the list view to be changed into an int, used in the for loop, add 1, then restring it and output it on my listView.
Please help :(
You say you want the text from a listview subitem converted to an int which is then used in a loop
so - first your creating your loop variable, i, then in your loop you're assigning to it potentially 3 different values 2 of which are negated by the, i++. None of it makes sense and you shouldn't be manipulating your loop variable like that (unless understand what you're doing).
if you move statements around a little..
int itemsToCheck = 10; // "Now I have this repeated about 10 times "
for (var item = 0; item < itemsToCheck; item++)
{
int i;
if (!Int32.TryParse(listView1.SelectedItems[item].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[item].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
Something along those lines is what you're looking for. I haven't changed what your code does with i, just added a loop count itemsToCheck and used a different loop variable so your loop variable and parsed value are not one in the same which will likely be buggy.
Maybe this give you an idea. You can start using this syntax from C# 7.0
var s = listView1.SelectedItems[0].SubItems[3].Text;
var isNumeric = int.TryParse(s, out int n);
if(isNumeric is true && n > 0){
questionNumberLabel.Text = s;
}
to shortcut more
var s = listView1.SelectedItems[0].SubItems[3].Text;
if(int.TryParse(s, out int n) && n > 0){
questionNumberLabel.Text = s;
}

Nested For loop multidimensional array search

Got a silly question im struggling with.
Im trying to step through a C# Multidimensional array using nested for loops, but i cant get the result i want and im thinking its just a stupid problem with my code.
string search = txtString.Text;
int iLoop;
int jloop;
int iResult = -1;
for (iLoop = 0; iLoop < sounds.GetLength(0) ; iLoop++)
{
for (jloop = 0; jloop < sounds.GetLength(1) ; jloop++)
{
string result;
result = sounds[iLoop,jloop];
if (result == search)
{
iResult = iloop;
}
}
}
if (iResult == -1)
{
MessageBox.Show("Result not found");
}
else
{
MessageBox.Show("Result found at position " + iResult);
}
}
It searches the array, and returns a positive result if the answer is found, but the result position is always "Result found at position 1".
What have i done wrong?
You are storing only one dimension (iResult). It can always be 1, but second dimension (jresult) can vary.
And just reminder for future projects in different languages. Try no to use if (result == search) for strings. Use Equal or Compare methods.
There are two indices to look up: jloop and iLoop , probably you will get various jloop values in the 2D array
Also check the name on the parameter you use, sometimes you call it iLoop, others iloop.
Be consistent! :)
I guess the answer is always in row 1, you just pring the i value, print also the j value jLoop.
Keep in mind that this code snippet will continue searching even after it has found a match. So in fact what you are finding is the last position of the matched text.
As an aside, perhaps instead of reporting only the matching iLoop, you could report both the matching iLoop and jLoop. Or, you can report a single index as iLoop * sounds.GetLength(0) + jLoop

"Contains" method doesn't seem to working the way it should

I'm trying to see if when a user enters some text it searches the array for any matches, and whatever doesn't match gets removed from the array;
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count; i++)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
}
I have some rubbish names in my array 'Rob Dob','Joe Bloggs', 'h h', 'ghg hgh', and if the search variable ended up being 'R', Joe Bloggs would get removed but 'h h' and 'ghg hgh' stay there, but there is no R involved there at all? any reason why>?!
You have to iterate backwards in order to remove from an array. Every time you remove an item, your array gets smaller. By going backwards, that fact does not matter.
string search = textBox1.Text;
for (int i = staffUsers.Count - 1; i >= 0 ; i--)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
}
The problem with your code is that you are removing items as you iterate over it. You remove an item, but keep iterating, even though the size of the array changes when you remove an item.
You need to reset your i value after you remove something. Alternatively, you need to use a built in to do the heavy lifting:
staffUsers.RemoveAll(i => !(i.staff_name.Contains(search)));
Uses a tiny LINQ expression to do the work. Remove all items where that predicate matches. i represents an item to apply the expression to. If that expression evaluates to true, away it goes.
Long story short, whenever you remove an item at index [i], you skip the item at index [i+1]. For example, if your array looks like:
{'Joe Bloggs', 'Rob Dobb', 'h h', 'gafddf'}; i=0
remove Joe Bloggs, which is at position 0.
{Rob Dobb', 'h h', 'gafddf'}; i=1
remove 'h h', which is at position 1
{Rob Dobb', 'gafddf'}; i=2
i is not less than yourArray.Count, so the loop stops. There is no position 2.
The quickest fix is to add i-- if you remove something from index [i]. In your case,
staffUsers.Remove(staffUsers[i]);
i--;
Hope this helps!
In each iteration of the loop, either remove an item or increment the counter, not both operations. Otherwise, you'll skip the next array element whenever you remove an array element:
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count;)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
else
{
i++;
}
}
The simplest way to solve the above problem is using LINQ.
Following code disentangle above problem.
string search = textBox1.Text;
staffUsers= (from user in staffUsers
where !user.Contains(search)
select user).ToArray<string>();
Note: I assumed staffUsers is array of string.
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count; i++)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
// reset the index one stepback
i--;
}
}

Time complexity of a powerset generating function

I'm trying to figure out the time complexity of a function that I wrote (it generates a power set for a given string):
public static HashSet<string> GeneratePowerSet(string input)
{
HashSet<string> powerSet = new HashSet<string>();
if (string.IsNullOrEmpty(input))
return powerSet;
int powSetSize = (int)Math.Pow(2.0, (double)input.Length);
// Start at 1 to skip the empty string case
for (int i = 1; i < powSetSize; i++)
{
string str = Convert.ToString(i, 2);
string pset = str;
for (int k = str.Length; k < input.Length; k++)
{
pset = "0" + pset;
}
string set = string.Empty;
for (int j = 0; j < pset.Length; j++)
{
if (pset[j] == '1')
{
set = string.Concat(set, input[j].ToString());
}
}
powerSet.Add(set);
}
return powerSet;
}
So my attempt is this:
let the size of the input string be n
in the outer for loop, must iterate 2^n times (because the set size is 2^n).
in the inner for loop, we must iterate 2*n times (at worst).
1. So Big-O would be O((2^n)*n) (since we drop the constant 2)... is that correct?
And n*(2^n) is worse than n^2.
if n = 4 then
(4*(2^4)) = 64
(4^2) = 16
if n = 100 then
(10*(2^10)) = 10240
(10^2) = 100
2. Is there a faster way to generate a power set, or is this about optimal?
A comment:
the above function is part of an interview question where the program is supposed to take in a string, then print out the words in the dictionary whose letters are an anagram subset of the input string (e.g. Input: tabrcoz Output: boat, car, cat, etc.). The interviewer claims that a n*m implementation is trivial (where n is the length of the string and m is the number of words in the dictionary), but I don't think you can find valid sub-strings of a given string. It seems that the interviewer is incorrect.
I was given the same interview question when I interviewed at Microsoft back in 1995. Basically the problem is to implement a simple Scrabble playing algorithm.
You are barking up completely the wrong tree with this idea of generating the power set. Nice thought, clearly way too expensive. Abandon it and find the right answer.
Here's a hint: run an analysis pass over the dictionary that builds a new data structure more amenable to efficiently solving the problem you actually have to solve. With an optimized dictionary you should be able to achieve O(nm). With a more cleverly built data structure you can probably do even better than that.
2. Is there a faster way to generate a power set, or is this about optimal?
Your algorithm is reasonable, but your string handling could use improvement.
string str = Convert.ToString(i, 2);
string pset = str;
for (int k = str.Length; k < input.Length; k++)
{
pset = "0" + pset;
}
All you're doing here is setting up a bitfield, but using a string. Just skip this, and use variable i directly.
for (int j = 0; j < input.Length; j++)
{
if (i & (1 << j))
{
When you build the string, use a StringBuilder, not creating multiple strings.
// At the beginning of the method
StringBuilder set = new StringBuilder(input.Length);
...
// Inside the loop
set.Clear();
...
set.Append(input[j]);
...
powerSet.Add(set.ToString());
Will any of this change the complexity of your algorithm? No. But it will significantly reduce the number of extra String objects you create, which will provide you a good speedup.

Search a file for a sequence of bytes (C#)

I'm writing a C# application in which I need to search a file (could be very big) for a sequence of bytes, and I can't use any libraries to do so. So, I need a function that takes a byte array as an argument and returns the position of the byte following the given sequence. The function doesn't have to be fast, it simply has to work. Any help would be greatly appreciated :)
If it doesn't have to be fast you could use this:
int GetPositionAfterMatch(byte[] data, byte[]pattern)
{
for (int i = 0; i < data.Length - pattern.Length; i++)
{
bool match = true;
for (int k = 0; k < pattern.Length; k++)
{
if (data[i + k] != pattern[k])
{
match = false;
break;
}
}
if (match)
{
return i + pattern.Length;
}
}
}
But I really would recommend you to use Knuth-Morris-Pratt algorithm, it's the algorithm mostly used as a base of IndexOf methods for strings. The algorithm above will perform really slow, exept for small arrays and small patterns.
The straight-forward approach as pointed out by Turrau works, and for your purposes is probably good enough, since you say it doesn't have to be fast - especially since for most practical purposes the algorithm is much faster than the worst case O(n*m). (Depending on your pattern I guess).
For an optimal solution you can also check out the Knuth-Morris-Pratt algorithm, which makes use of partial matches which in the end is O(n+m).
Here's an extract of some code I used to do a boyer-moore type search. It's mean to work on pcap files, so it operates record by record, but should be easy enough to modify to suit just searching a long binary file. It's sort of extracted from some test code, so I hope I got everything for you to follow along. Also look up boyer-moore searching on wikipedia, since that is what it's based off of.
int[] badMatch = new int[256];
byte[] pattern; //the pattern we are searching for
//badMath is an array of every possible byte value (defined as static later).
//we use this as a jump table to know how many characters we can skip comparison on
//so first, we prefill every possibility with the length of our search string
for (int i = 0; i < badMatch.Length; i++)
{
badMatch[i] = pattern.Length;
}
//Now we need to calculate the individual maximum jump length for each byte that appears in my search string
for (int i = 0; i < pattern.Length - 1; i++)
{
badMatch[pattern[i] & 0xff] = pattern.Length - i - 1;
}
// Place the bytes you want to run the search against in the payload variable
byte[] payload = <bytes>
// search the packet starting at offset, and try to match the last character
// if we loop, we increment by whatever our jump value is
for (i = offset + pattern.Length - 1; i < end && cont; i += badMatch[payload[i] & 0xff])
{
// if our payload character equals our search string character, continue matching counting backwards
for (j = pattern.Length - 1, k = i; (j >= 0) && (payload[k] == pattern[j]) && cont; j--)
{
k--;
}
// if we matched every character, then we have a match, add it to the packet list, and exit the search (cont = false)
if (j == -1)
{
//we MATCHED!!!
//i = end;
cont = false;
}
}

Categories

Resources