C# Comparing an array of string values - c#

I am creating static class using a static method that is comparing a string filled with selections from a user input, and a predefined array of what is "supposed to be the inputs";
My concern is the placement of the pre-defined array within the class and if the correct data type to be used is actually an array or a dictionary.
I will have roughly 150 strings max in the pre-defined and ready to compare against string array.
here is what I have so far.
public static class SelectionMatchHelper
{
static readonly string[] predefinedStrings = {"No answer", "Black", "Blonde"};
public readonly static bool SelectionMatch(string[] stringsWeAreComparingAgainst, int validCount)
{
int numberOfMatches = 0;
for (int x = 0; x < "length of string array"; x++)
{
//will loop through and check if the value exists because the array to match against will not always have the same index length
numberOfMatches += 1;
}
numberOfMatches.Dump();
if (numberOfMatches == 0 || numberOfMatches < validCount || numberOfMatches > validCount) return false;
return true;
}
}
what this basically does, is based on the number of parameters the user must fulfill, the method gets the matches, if it doesn't equal that amount then it returns false. The input the user is using is a drop down, so this is only being used to make sure my values aren't tampered with prior to saving.
My question is what data type is best used for this scenario an string array/list or dictionary? The second question is where should that be placed in order to avoid a thread issue, inside the method or out?
EDIT - I just want to add that the pre-defined values would remain the same, so I would end up making that field a readonly const value.
EDIT 2 - Just re-checked my code I wont be using CompareOrdinal because I totally forgot the part where the order matters. So it will be a key look up. So I will remove the inside of the method so people don't confused. The main question is still the same.
Thanks for your help everyone.

From readability point of view HashSet is the best type as it specifically exists for "item is present in the set".
static readonly HashSet<string> predefinedStrings = new HashSet<string>(
new []{"No answer", "Black", "Blonde"},
StringComparer.Ordinal);
if (predefinedStrings.Contains("bob"))....
Fortunately HashSet also thread safe for read-only operations, provides O(1) check time and supports case insensitive comparison if you need one.

Related

How do you return an array in a procedure back to the main procedure in C#?

static string readfileName(string[] name)
{
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
int counter = 0;
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
name[counter] = ln;
counter++;
}
}
file.Close();
return name;
}
}
This is the procedure I'm currently trying to return the array name[50] but the compile time error I can't fix states
"Error CS0029 Cannot implicitly convert type 'string[]' to 'string' "
You don't need to. Your main method passed the array to this method, this method filled it. It doesn't need to hand it back because the object pointed to by your 'name` variable is the same object as pointed to by the original variable in the main method; your main method already has all the array data:
static void Main(){
var x = new string[10];
MyMethod(x);
Console.Write(x[0]); //prints "Hello"
}
static void MyMethod(string[] y){
y[0] = "Hello";
}
In this demo code above we start out with an array of size 10 that is referred to by a variable x. In memory it looks like:
x --refers to--> arraydata
When you call MyMethod and pass x in, c# will create another reference y that points to the same data:
x --refers to--> arraydata <--refers to-- y
Now because both references point to the same area of memory anything that you do with y, will also affect what x sees. You put a string (like I did with Hello) in slot 0, both x and y see it. When MyMethod finishes, the reference y is thrown away, but x survives and sees all the changes you made when working with y
The only thing you can't do is point y itself to another different array object somewhere else in memory. That won't change x. You can't do this:
static void MyMethod(string[] y){
y = new string[20];
}
If you do this your useful reference of x and y pointing to the same area of memory:
x ---> array10 <--- y
Will change to:
x ---> array10 y ---> array20
And then the whole array20 and the y reference will be thrown away when MyMethod finishes.
The same rule applies if you call a method that supplies you an array:
static void MyMethod(string[] y){
y = File.ReadAllLines("some path"); //this also points y away to a new array made by ReadAllLines
}
It doesn't matter how or who makes the new array. Just remember that you can fiddle with the contents of an object pointed to by y all you like and the changes will be seen by x, but you can't change out the entire object pointed to by y and hope x will see it
in that case you WOULD have to pass it back when you're done:
static string[] MyMethod(string[] y){
y = new ...
return y;
}
And the main method would have to capture the change:
Main(...){
string[] x = new string[10];
string[] result = MyMethod(x);
}
Now, while I'm giving this mini lesson of "pass by reference" and "pass by value" (which should have been called "pass by original reference" and "pass by copy of reference") it would be useful to note that there is a way to change things so MyMethod can swap y out for a whole new object and x will see the change too.
We don't really use it, ever; there is rarely any need to. Just about the only time it's used is in things like int.Parse. I'm telling you for completeness if education so that if you encounter it you understand it but you should always prefer a "change the contents but not the whole object" or a "if you make a new object pass it back" approach
By marking the y argument with the ref keyword, c# wont make a copy of the reference when calling the method, it will use the original reference and temporarily allow you to call it y:
static void MyMethod(ref string[] y){
y = new array[20];
}
Our diagram:
x ---> array10data
Temporarily becomes:
x a.k.a y ---> array10data
So if you point y to a new array, x experiences the change too, because they're the same reference; y is no longer a different reference to the same data
x a.k.a y ---> array20data
Like I say, don't use it- we always seek to avoid it for various reasons.
Now, I said at the start "you don't need to" - by that, and for the reasons above, I meant you don't need to return anything from this method
Your method receives the array it shall fill (from the file) as a parameter; it doesn't make a new array anywhere so there isn't any need to return the array when done. It will just put any line longer than 4 chars into an array slot. It could then finish without returning anything and the method that called this method will see the changes it made in the array. This is just like my code, where MyMethod changes slot 0 of the array, MyMethod was declared as void so it didn't need to make a return statement , and my Main method god could still see the Hello that I put in the array. In the same vein, your Main method will see all those lines from the file if you make your ReadFileName method (which should perhaps be called FillArray) because it fills the array called name
The most useful thing your method could return is actually an integer saying how many lines were read; the array passed in is of a fixed size. You can't resize it because that entails making a new array which won't work for all those reasons I talked about above. If you were to make a new array and return it there wouldn't be any point in passing an array in.
There are thus several ways we could improve this code but to my mind they come down to two:
don't pass an array in; let this method make a new array and return it. The new array passed back can be exactly sized to fit
keep with the "pass an array in" idea and return an integer of how many lines were actually read instead
For the second idea (which is the simplest to implement) you have to change the return type to int:
static int ReadFileName(string[] name)
And you have to return that variable you use to track which slot to put the next thing in, counter. Counter is always 1 greater than the number of things you've stored so:
return counter - 1;
Your calling method can now look like:
string[] fileData = new string[10000]; //needs to be big enough to hold the whole file!
int numberOfLinesRead = ReadFileName(fileData);
Can you see now why ReadFileName is a bad name for the method? Calling it FillArrayFromFile would be better. This last line of code doesn't read like a book, it doesn't make sense from a natural language perspective. Why would something that looks like it reads a file name (if that even makes sense) take an array and return an int - calling it ReadFileName makes it sound more like it searches an array for a filename and returns the slot number it was found in. Here ends the "name your methods appropriately 101"
So the other idea was to have the Read method make its own array and return it. While we are at it, let's call it ReadFileNamed, and have it take a file path in so it's not hard coded to reading just that one file. And we will have it return an array
static string[] ReadFileNamed(string filepath)
^^^^^^^^ ^^^^^^^^^^^^^^^
the return type the argument passed in
Make it so the first thing it does is declare an array big enough to hold the file (there are still problems with this idea, but this is programming 101; I'll let them go. Can't fix everything using stuff you haven't been taught yet)
Put this somewhere sensible:
string lines = new string[10000];
And change all your occurrences of "name" to be "lines" instead - again we name our variables we'll just like we name our methods sensibly
Change the line that reads the fixed filename to use the variable name we pass in..
using (StreamReader file = new StreamReader(filepath))
At the end of the method, the only thing left to do is size the array accurately before we return it. For a 49 line file, counter will be 50 so let's make an array that is 49 big and then fill it using a loop (I doubt you've been shown Array.Copy)
string[] toReturn = new string[counter-1];
for(int x = 0; x < toReturn.Length; x++)
toReturn[x] = lines[x];
return toReturn;
And now call it like this:
string[] fileLines = ReadFileNamed("student marks.txt");
If you're looking to return name[50] and you know that will be populated, why not go with:
static string readfileName(string[] name)
{
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
int counter = 0;
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
name[counter] = ln;
counter++;
}
}
file.Close();
return name[50];
}
}
You're getting the error because your method signature indicates that you're going to return a string, but you're defining name as a string[] in the argument. If you simply select a single index of your array in the return statement, you'll only return a string.
You have defined your method to return a string, yet the code inside is returning name, which is a string[]. If you want it to return a string[], then change the signature to specify that:
static string[] ReadFileName(string[] name)
However, since your method is only populating the array that was passed in, it's not really necessary to return the array, since the caller already has a reference to the array we're modifying (they passed it to our method in the first place).
There is a potential problem here, though
We're expecting the caller to pass us an array of the appropriate length to hold all the valid lines from the file, yet that number is unknown until we read the file. We could return an array of the size they specified with either empty indexes at the end if it was too big, or incomplete data if it was too small, but instead we should probably just return them a new array, and not require them to pass one to us.
Note that it's easier to use a List<string> instead of a string[], since lists don't require any knowledge of their size at instantiation (they can grow dynamically). Also, we no longer need a counter variable (since we're using the Add method of the list to add new items), and we can remove the file.Close() call since the using block will call that automatically (one of the cool things about them):
static string[] ReadFileName()
{
List<string> validLines = new List<string>();
using (StreamReader file = new StreamReader("StudentMarks.txt"))
{
string ln;
while ((ln = file.ReadLine()) != null)
{
if (ln.Length > 4)
{
validLines.Add(ln);
}
}
}
return validLines.ToArray();
}
And we can simplify the code even more if we use some static methods of the System.IO.File class:
static string[] ReadFileName()
{
return File.ReadLines("StudentMarks.txt").Where(line => line.Length > 4).ToArray();
}
We could also make the method a little more robust by allowing the caller to specify the file name as well as the minimum line length requirement:
static string[] ReadFileName(string fileName, int minLineLength)
{
return File.ReadLines(fileName)
.Where(line => line.Length >= minLineLength).ToArray();
}
Well, you are trying to do several thing in one method:
Read "StudentMarks.txt" file
Put top lines into name existing array (what if you have too few lines in the file?)
return 50th (magic number!) item
If you insist on such implementation:
using System.Linq;
...
static string readfileName(string[] name)
{
var data = File
.ReadLines("StudentMarks.txt")
.Where(line => line.Length > 4)
.Take(name.Length);
int counter = 0;
foreach (item in data)
if (counter < name.Length)
name[counter++] = item;
return name.Length > 50 ? name[50] : "";
}
However, I suggest doing all things separately:
// Reading file lines, materialize them into string[] name
string[] name = File
.ReadLines("StudentMarks.txt")
.Where(line => line.Length > 4)
// .Take(51) // uncomment, if you want at most 51 items
.ToArray();
...
// 50th item of string[] name if any
string item50 = name.Length > 50 ? name[50] : "";
Edit: Splitting single record (name and score) into different collections (name[] and score[]?) often is a bad idea;
the criterium itself (line.Length > 4) is dubious as well (what if we have Lee - 3 letter name - with 187 score?).
Let's implement Finite State Machine with 2 states (when we read name or score) and read (name, score) pairs:
var data = File
.ReadLines("StudentMarks.txt")
.Select(line => line.Trim())
.Where(line => !string.IsNullOrEmpty(line));
List<(string name, int score)> namesAndScores = new List<(string name, int score)>();
string currentName = null;
foreach (string item in data) {
if (null == currentName)
currentName = item;
else {
namesAndScores.Add((currentName, int.Parse(item)));
currentName = null;
}
}
Now it's easy to deal with namesAndScores:
// 25th student and his/her score:
if (namesAndScores.Count > 25)
Console.Write($"{namesAndScores[25].name} achieve {namesAndScores[25].score}");

Index out of range using lists and for loop

I have this loop here, which for each question it is supposed to create, it generates and then formats a 'worded question' from an array of questions, such as; 'What is the sum of {0} + {1}?'. This loop then formats it, adds the worded question and the answer to an array.
// Use for loop to create the correct amount of questions
for (int i = 0; i < Data.Questions.numQuestions; i++)
{
Random rnd = new Random();
Data.Questions.Sum sum = new Data.Questions.Sum();
// Create part one and part two of the question using random numbers
// ex. 3 + 5
// 3 = partOne, 5 = partTwo
int partOne = rnd.Next(Data.Questions.Sum.min, Data.Questions.Sum.max);
int partTwo = rnd.Next(Data.Questions.Sum.min, Data.Questions.Sum.max);
// Randomly select one of the word questions
string fullQuestion = Data.Questions.Sum.wordedQuestions[rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length)];
// Format the string with the generated numbers
fullQuestion = string.Format(fullQuestion, partOne, partTwo);
// Set out-of-class variables to be displayed to the user
Data.Questions.Sum.questions[i] = fullQuestion;
Data.Questions.Sum.answers[i] = partOne + partTwo;
}
Both Data.Questions.Sum.questions and Data.Questions.Sum.answers are List<string>'s and List<int>'s.
However, when this loop is run, with i = 0, I am thrown;
System.ArgumentOutOfRangeException: 'Index was out of range. Must be
non-negative and less than the size of the collection. Parameter name:
index'
Does anyone know what I'm doing wrong? As far as I know lists are dynamic, and I've defined like this;
// Arrays containing all questions and answers
// used to display questions and check answers
public static List<string> questions = new List<string>();
public static List<int> answers = new List<int>();
Also, to clarify, I do not want to use .Add(), as I have a settings panel which when you hit apply, re-runs this loop so the questions are up to date to the current settings. I need the loop to override the previous values.
Edit:
When using arrays, the better option here, I get;
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
On assigning Data.Questions.Sum.answers[i], after assigning the array like so; public static int[] answers {};
If you can't .Add() in this lists - create a copy of this lists and .Add() there. Lists has special for that kind of thing: new List<T>(IEnumerable<T>)
If you need to dynamically scale the collection, but you also need to iterate over it multiple times, then you'll need to check whether it is large enough to either insert, or just update.
You can do this with an extension method such as this...
public static class ListExtentions
{
public static void AddOrUpdate<T>(this List<T> that, int index, T value)
{
if (that.Count > index)
{
that[index] = value;
}
else
{
that.Add(value);
}
}
}
...which can then be called like this...
list.AddOrUpdate(index, value);
...however you can make things easier for yourself if you know how many questions you are going to have to start with.
If the number of questions changes when your UI changes, then you will also have to deal with the issue have scaling down the collection to ensure old elements are removed, which is much simpler if you just re-instantiate the collections every time you need to regenerate the questions / answers.
This is likly to be cause of your problem,(I have asked for clarification in comments where you didn't reply).
Still putting this as an answer, as it is potential error spot and you need to fix this.
As you mentioned you are facing this exception on i=0. there are high chanced that this is every time case not any specific case.
If Data.Questions.Sum.questions is empty then, Data.Questions.Sum.questions[i] = fullQuestion; , will surely throw such exception. Same way for Data.Questions.Sum.answers too.
In such case, you must use .Add() to insert into list.
so your code should be,
if (Data.Questions.Sum.questions.Count > i)
Data.Questions.Sum.questions[i] = fullQuestion;
else
Data.Questions.Sum.questions.Add(fullQuestion);
But if they are not empty, it must not be the cause of this exception.
One more thing i have noticed in your code is Data.Questions.Sum.wordedQuestions.
Even if you have valid list (here Data.Questions.Sum.wordedQuestions) - as you have Length prop, it must be Array not list.
If it is empty, while doing this
string fullQuestion = Data.Questions.Sum.wordedQuestions[rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length)];
this line will surely throw
An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
as you are trying to get 0th index's data from it.
So before fetching data from list or array, first you need to check if it does have data init, and also it does have that index which you are asking.
something like
string fullQuestion = string.Empty;
if (Data.Questions.Sum.wordedQuestions != null &&
Data.Questions.Sum.wordedQuestions.Length > 0)
{
//here the way you are creating random number,
// you are assured about index is present in array.
int indexForWordedQuestion = rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length);
fullQuestion = Data.Questions.Sum.wordedQuestions[indexForWordedQuestion];
}

Sorting INT variables C#

I am just beginning with programming in c#;
I got a list of int variables that I want to sort, and find the number 1.
int Weapon_Count1, Weapon_Count2, Weapon_Count3, Weapon_Count4, Weapon_Count5, Weapon_Count6, Weapon_Count7, Weapon_Count8, Weapon_Count9
do I need to do this with an array?
By using the yellow book of C# I found out how to make an array, but I can't figure out how to assign the variables to the array.
int [] Weapon_Count = new int [11] ;
for ( int i=0; i<11; i=i+1)
{
Weapon_Count [i] = ??? ;}
I hope this does make sense..
Please let me explain how to use a C#-array.
This creates an unitialized integer-array with 5 elements:
int[] a1= new int[5];
Assigning values 9,8,7,6 and 5:
(Please note that only indexes from 0 to 4 can be used. Index 5 is not valid.)
a1[0]=9;
a1[1]=8;
a1[2]=7;
a1[3]=6;
a1[4]=5;
The same can also achieved with just one line:
int[] a1= new int[] {9,8,7,6,5};
This might help you.
// Declaring the array
int[] Weapon_Count;
// Initializing the array with a size of 11
Weapon_Count = new int[11];
// Adding values to the array
for (int i = 0; i < Weapon_Count.Length; i++)
{
Weapon_Count[i] = i + 100;
}
// Printing the values in the array
for (int i = 0; i < Weapon_Count.Length; i++)
{
Console.WriteLine(Weapon_Count[i]);
}
// Same thing with a list
// Daclare and initializing the List of integers
List<int> weapon_list = new List<int>();
// Adding some values
weapon_list.Add(1);
weapon_list.Add(2);
weapon_list.Add(3);
weapon_list.Add(4);
weapon_list.Add(5);
// Printing weapin_list's values
for (int i = 0; i < weapon_list.Count; i++)
{
Console.WriteLine(weapon_list[i]);
}
// This is just for the console to wait when you are in debug mode.
Console.ReadKey();
Dont forget to include the using statment if you want to use lists (in short hand - dynamic arrays that can change in size.)
using System.Collections.Generic;
The easiest way to do this, assuming there is a finite list of variables to check, would be to throw them into a temporary array and call either Max() or Min() from the System.Linq namespace.
int maxCount = new int[] { Weapon_Count1, Weapon_Count2, Weapon_Count3, Weapon_Count4, Weapon_Count5, Weapon_Count6, Weapon_Count7, Weapon_Count8, Weapon_Count9 }.Max(); // or .Min()
EDIT
If you still want to get those variables into an array, I would recommend using a System.Collections.Generic.List which has a dynamic size and helper methods such as .Add() to simplify things. Lists can also be used with Linq functions similar to the first part of my answer. See Dot Net Perls for some really good examples on different C# data types and functions.
EDIT 2
As #kblok says, you'll want to add using System.Linq; at the top of your file to gain access to the functions such as Max and Min. If you want to try using the List type, you'll need to add using System.Collections.Generic; as well. If you're in Visual Studio 2017 (maybe 2015 as well?) you can type out the data type and then hit Ctrl + . to get suggestions for namespaces that might contain that data type.
Before we start, you might edit your array to look like this:
int[] weapons = { Weapon_Count1, Weapon_Count2, Weapon_Count3, Weapon_Count4, Weapon_Count5, Weapon_Count6, Weapon_Count7, Weapon_Count8, Weapon_Count9 };
This means that you've created an array called weapons and it is holding integer values.
After you did this, lets find out which element in your array has value of number one.
To find which value has value "1" we must look at each element in array, and we might do that on few ways but I would like recommend foreach or for loop, in this case I will choose foreach loop.
foreach(var item in weapons)
{
if (item == 1)
//Do something
}
This above means, loop throught all of my elements, and in case some of them is equal to number one please do something..
P.S
(I may advice to create one variable which will hold an element which has value '1' and when you find it in a loop assing that variable to that element, and later you can do whatever you want with that variable.. and if you think there will be more elements with value of number one and you need all of them, instead of variable I mentioned above you will create list or array to hold all of your elements and also you can do later with them whatever you want to.)
Thanks and if you are interested in this kind of solution, leave me a comment so let me help you till the end to solve this if you are still struggling.

How to check if random values are unique?

C # code:
I have 20 random numbers between 1-100 in an array and the program should check if every value is unique. Now i should use another method which returns true if there are only unique values in the array and false if there are not any unique values in the array. I would appreciate if someone could help me with this.
bool allUnique = array.Distinct().Count() == array.Count(); // or array.Length
or
var uniqueNumbers = new HashSet<int>(array);
bool allUnique = uniqueNumbers.Count == array.Count();
A small alternative to #TimSchmelters excellent answers that can run a bit more efficient:
public static bool AllUniq<T> (this IEnumerable<T> data) {
HashSet<T> hs = new HashSet<T>();
return data.All(hs.Add);
}
What this basically does is generating a for loop:
public static bool AllUniq<T> (this IEnumerable<T> data) {
HashSet<T> hs = new HashSet<T>();
foreach(T x in data) {
if(!hs.Add(x)) {
return false;
}
}
return true;
}
From the moment one hs.Add fails - this because the element already exists - the method returns false, if no such object can be found, it returns true.
The reason that this can work faster is that it will stop the process from the moment a duplicate is found whereas the previously discussed approaches first construct a collection of unique numbers and then compare the size. Now if you iterate over large amount of numbers, constructing the entire distinct list can be computationally intensive.
Furthermore note that there are more clever ways than generate-and-test to generate random distinct numbers. For instance interleave the generate and test procedure. Once a project I had to correct generated Sudoku's this way. The result was that one had to wait entire days before it came up with a puzzle.
Here's a non linq solution
for(int i=0; i< YourArray.Length;i++)
{
for(int x=i+1; x< YourArray.Length; x++)
{
if(YourArray[i] == YourArray[x])
{
Console.WriteLine("Found repeated value");
}
}
}

Can I retrieve the stored value x in a hashset given an object y where x.Equals(y)

[TestFixture]
class HashSetExample
{
[Test]
public void eg()
{
var comparer = new OddEvenBag();
var hs = new HashSet<int>(comparer);
hs.Add(1);
Assert.IsTrue(hs.Contains(3));
Assert.IsFalse(hs.Contains(0));
// THIS LINE HERE
var containedValue = hs.First(x => comparer.Equals(x, 3)); // i want something faster than this
Assert.AreEqual(1, containedValue);
}
public class OddEvenBag : IEqualityComparer<int>
{
public bool Equals(int x, int y)
{
return x % 2 == y % 2;
}
public int GetHashCode(int obj)
{
return obj % 2;
}
}
}
As well as checking if hs contains an odd number, I want to know what odd number if contains. Obviously I want a method that scales reasonably and does not simply iterate-and-search over the entire collection.
Another way to rephrase the question is, I want to replace the line below THIS LINE HERE with something efficient (say O(1), instead of O(n)).
Towards what end? I'm trying to intern a laaaaaaaarge number of immutable reference objects similar in size to a Point3D. Seems like using a HashSet<Foo> instead of a Dictionary<Foo,Foo> saves about 10% in memory. No, obviously this isn't a game changer but I figured it would not hurt to try it for a quick win. Apologies if this has offended anybody.
Edit: Link to similar/identical post provided by Balazs Tihanyi in comments, put here for emphasis.
The simple answer is no, you can't.
If you want to retrieve the object you will need to use a HashSet. There just isn't any suitable method in the API to do what you are asking for otherwise.
One optimization you could make though if you must use a Set for this is to first do a contains check and then only iterate over the Set if the contains returns true. Still you would almost certainly find that the extra overhead for a HashMap is tiny (since essentially it's just another object reference).

Categories