with my code, I'm supposed to be able to call a question from my file at random and not reuse that question again.
But for some reason I'm stuck in the while loop and after a few hours of searching I still can't see why, so I hope you guys can help me out.
(he gets stuck after he generated 8 questions)
Code (for the generatequestion one, if you need more I can paste it but I hope this gives enough information):
lineCount = File.ReadLines(pad).Count();
questions = new string[lineCount, 2];
public string GenerateQuestion(int total)
{
if (total <= 10)
{
Random ran = new Random();
questionNumber = ran.Next(lineCount);
while (previousQuestions.Contains(questionNumber))
{
questionNumber = ran.Next(lineCount);
}
}
previousQuestions[questionCount] = questionNumber;
questionCount++;
return questions[questionNumber, 0];
}
You could make a method to return the lines in the file in random order.
public string[] GetQuestionsInRandomOrder()
{
var lines = File.ReadAllLines("test.txt");
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToArray();
return lines;
}
Then as you use the questions you can remove them from the array.
var isRemoved = Array.remove(array, item);
https://msdn.microsoft.com/en-us/library/vstudio/bb397721%28v=vs.100%29.aspx
This is easier that what I had before and will give greater control. I would create a class for this and this example is a starting point. You can add more functionality as you go along. By having a class to do all this work you can add methods to do additional features later on without having to rewrite code.
public class Logic
{
public List<string> AllQuestionsInRandomOrder { get; set; }
public List<string> QuestionsThatHaveBeenRemoved { get; set; }
public Logic()
{
QuestionsThatHaveBeenRemoved = new List<string>();
AllQuestionsInRandomOrder = GetQuestionsInRandomOrder().ToList();
}
public string GetUnusedQuestion()
{
var question =
AllQuestionsInRandomOrder.FirstOrDefault(x => !QuestionsThatHaveBeenRemoved.Contains(x));
QuestionsThatHaveBeenRemoved.Add(question);
return question;
}
public IEnumerable<string> GetQuestionsInRandomOrder()
{
var lines = File.ReadAllLines("test.txt").ToList();
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToList();
return lines;
}
public void RemoveQuestion(List<string> questions, string questionToRemove)
{
questions.Remove(questionToRemove);
}
}
I would avoid using loops and break the problem into separate methods or functions. It will make it easier to debug. This implementation does not separate the questions and answers out, so you will need to add a method that returns a dictionary of the questions and answers.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var questions = new Questions();
var firstQuestion = questions.GetUnusedQuestion();
Console.WriteLine(firstQuestion);
}
}
class Questions
{
public string FileName { get; set; }
public static List<string> AllQuestionsAndAnswersFromFile { get; set; }
public List<string> AllQuestionsInRandomOrder { get; set; }
public List<string> QuestionsThatHaveBeenRemoved { get; set; }
public Questions()
{
FileName = "text.txt";
QuestionsThatHaveBeenRemoved = new List<string>();
AllQuestionsAndAnswersFromFile = new List<string>();
ReadQuestionsFromFile();
AllQuestionsInRandomOrder = GetQuestionsInRandomOrder().ToList();
}
public string GetUnusedQuestion()
{
var question =
AllQuestionsInRandomOrder.FirstOrDefault(x => QuestionsThatHaveBeenRemoved.Contains(x));
QuestionsThatHaveBeenRemoved.Add(question);
return question;
}
private static IEnumerable<string> GetQuestionsInRandomOrder()
{
var lines = AllQuestionsAndAnswersFromFile;
var rnd = new Random();
lines = lines.OrderBy(line => rnd.Next()).ToList();
return lines;
}
public void RemoveQuestion(List<string> questions, string questionToRemove)
{
questions.Remove(questionToRemove);
}
public void ReadQuestionsFromFile()
{
using (var reader = new StreamReader(FileName, Encoding.Default))
{
var text = reader.ReadToEnd();
var lines = text.Split('=');
foreach (var s in lines)
{
AllQuestionsAndAnswersFromFile.Add(s);
}
}
}
}
}
When you run out of questions, let's see what
while (previousQuestions.Contains(questionNumber))
{
questionNumber = ran.Next(lineCount);
}
actually does:
Is questionNumber one of the questions we already asked?
Yes, get a new question number. (because we've asked all the questions)
Is questionNumber one of the questions we already asked?
yes, get a new question number. (because we've asked all the questions)
A solution in this case would be to shuffle your questions that you return, removing them as you grab them.
Related
I wrote a short application, but I ran into the problem of writing a unit test Method MaximumRowSum_DefectiveLines. Please tell me how I should act, my class for testing
public class OutputtingStrings
{
public class MaxSumLineResult
{
public int MaxSumLineIndex { get; set; }
public List<string> DefectiveLines { get; set; }
public override string ToString()
{
return $"Line number with maximum sum of elements: { MaxSumLineIndex + 1}"; /* + "\n" +
$"Defective lines:{string.Join("\n", DefectiveLines)}";*/
}
}
public static bool IsValidateFileExist(string filePath)
{
if (File.Exists(filePath))
{
return true;
}
else
{
return false;
}
}
public MaxSumLineResult MaximumRowSum_DefectiveLines(string[] fileData)
{
List<string> defectiveLines = new List<string>();
int lineNumber = 0;
var indexOfLines = new Dictionary<int, double>();
foreach (var line in fileData)
{
NumberStyles style = NumberStyles.Number;
CultureInfo culture = CultureInfo.CreateSpecificCulture("en-GB");
var stringElements = line.Split(",", StringSplitOptions.RemoveEmptyEntries);
if (stringElements.Any(n => double.TryParse(n, style, culture, out var number)))
{
indexOfLines.Add(lineNumber, stringElements.Sum(n =>
{
return double.Parse(n, style, culture);
}));
}
else
{
defectiveLines.Add(line);
}
lineNumber++;
}
var maxSumLineIndex = indexOfLines.FirstOrDefault(x =>
x.Value == indexOfLines.Values.Max()).Key;
var resultLines = new MaxSumLineResult
{
MaxSumLineIndex = maxSumLineIndex,
DefectiveLines = defectiveLines
};
return resultLines;
}
}
My unit testing class:
[TestClass]
public class UnitTestOutputtingStrings
{
[TestMethod]
public void Should_FindingMaximumRowSum_TheFileIsValidAndReadable()
{
/* Arrange*/
var maxsumlineresult = new MaxSumLineResult();
var sut = new OutputtingStrings();
/* Act*/
/* Assert*/
}
}
I have read the book "The Art of Unit Testing. With Examples in C#". I understand the principles, but I do not know how to work with complex classes. Thank you guys in advance, I will be glad to every answer or link to a source with materials on unit testing.
For sure in the method, there is too much responsibility.
I think that the good idea to start is to divide the method into more than one smaller methods. After that unit testing of that method would be simpler to be done.
using System;
using System.Collections.Generic;
using System.IO;
namespace _2._1
{
class Narys
{
public string Vardas { get; set; }
public string Pavarde { get; set; }
public double Pinigai { get; set; }
public Narys()
{
}
public Narys(string vardas, string pavarde, double pinigai)
{
Vardas = vardas;
Pavarde = pavarde;
Pinigai = pinigai;
}
List<Narys> DuomenuSkaitymas()
{
List<Narys> nariai = new List<Narys>();
string[] eilutes = File.ReadAllLines(#"nariai.txt");
foreach (string eilute in eilutes)
{
string[] duomenys = eilute.Split(' ');
string vardas = duomenys[0];
string pavarde = duomenys[1];
double pinigai = double.Parse(duomenys[2]);
Narys narys = new Narys(vardas, pavarde, pinigai);
nariai.Add(narys);
}
return nariai;
}
void DuomenuIrasymas(List<Narys> nariai)
{
string[] eilutes = new string[nariai.Count];
for (int i = 0; i < nariai.Count; i++)
{
eilutes[i] = String.Format("{0} {1} {2}", nariai[i].Vardas, nariai[i].Pavarde, nariai[i].Pinigai);
}
File.WriteAllLines(#"nariaiAts.txt", eilutes);
}
void DuomenuParodymas(List<Narys> nariai)
{
foreach (Narys narys in nariai)
{
Console.WriteLine("Vardas: {0}\nPavarde: {1}\nPinigai: {2}", narys.Vardas, narys.Pavarde, narys.Pinigai);
}
}
}
class Program
{
static void Main(string[] args)
{
Program p = new Program();
List<Narys> nariai = p.DuomenuSkaitymas();
p.DuomenuIrasymas(nariai);
}
}
}
And why i'm getting those errors ?
I think it should work, but it isn't, so i guess you will be able to solve this sh*t. Also, i'm studying in university and i am doing this by example and it really should work. I think there should be enough info for you guys.
Just look at your code:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
List<Narys> nariai = p.DuomenuSkaitymas();
p.DuomenuIrasymas(nariai);
}
}
You are declaring the class Program that contains only a static method. Then you instantiate that class in variable p. Then you are trying to access some DuomenuSkaitymas method of it. But it contains only a static method. So how should this work?
You probably wanted to instantiate class Narys in p instead of Program.
Just a pro tip: never use identifier names in your local language, even if it looks more understandable at first. Your code might well end in the hands of others who don't understand your language. Believe me, you will choose better identifiers if you want others to understand your code - and that will be valuable to you too.
Your class Program just contains the main() method, so the IDE informs you that Program does not contains DuomenuSkaitymas method.
That method (DuomenuSkaitymas) , is defined in the Nary's class, so probably you need to modify you main method to
class Program
{
static void Main(string[] args)
{
Narys p = new Narys();
List<Narys> nariai = p.DuomenuSkaitymas();
p.DuomenuIrasymas(nariai);
}
}
I didn't check what the program means itself just clarifying your CS1061, so if there is other problems try to look closely to the code and understand what it means. If any other issue non related to this CS1061 arises after that you could try to post a new question with your issues or ideas about it.
I am entering code into a list. However, when I try to iterate through the code in a method call, nothing is being returned.
The problem code is found in "public class iterating". I am not sure why it won't execute.
Basically, I would like someone to enter information. Once the information has been entered, I would then like the user to iterate through the list via a method call.
using System;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Iterating
{
List<String> employees = new List<String>();
public void Test2()
{
//This is where I am trying to iterate//
for (int i = 0; i < employees.Count; i++)
{
Console.WriteLine(employees[i]);
}
}
}
public class Testing
{
public void Test1()
{
while (true)
{
List<String> employees = new List<String>();
Regex regex = new Regex(#"((.{5})-\d{2,5}-\d{2,5})|(#.*.com)");
Console.WriteLine("Please enter an e-mail");
string input = Console.ReadLine();
if(string.Equals(input, "quit", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("You have quit the program");
break;
}
else if (match.Success)
{
Console.WriteLine(match.Value);
employees.Add(match.Value);
}
}
}
}
public class Program
{
public static void Main()
{
Testing T1 = new Testing();
T1.Test1();
Iterating I1 = new Iterating();
I1.Test2();
}
}
You don't get any output since
Every loop in the collection stage uses a new employees list.
Your two classes do not share the same employees list - they each have their own separate list.
If you want the iterating class to print the contents of a list, you need to pass it the list in question.
A partial example:
public class Program {
public static void Main() {
List<String> employees = new List<String>();
Testing T1 = new Testing();
T1.Test1(employees);
Iterating I1 = new Iterating();
I1.Test2(employees);
}
}
You would modify your test methods to use the passed list rather than create new ones
public class Iterating {
List<String> employees = new List<String>();
public void Test2()
{
//This is where I am trying to iterate//
for (int i = 0; i < employees.Count; i++)
{
Console.WriteLine(employees[i]);
}
}
}
employees is always empty since this doesn't have anything to do with Testing.employees you might want to remove it and pass the list into Test2 or into the constructor.
Also, Testing.employees should be outside the while look
You can redesign your class like this
public class Iterating
{
public void Test2(List<String> employees)
{
//This is where I am trying to iterate//
for (int i = 0; i < employees.Count; i++)
{
Console.WriteLine(employees[i]);
}
}
}
public class Testing
{
public List<String> Test1()
{
List<String> employees = new List<String>();//this should be outside the while loop
while (true)
{
Regex regex = new Regex(#"((.{5})-\d{2,5}-\d{2,5})|(#.*.com)");
Console.WriteLine("Please enter an e-mail");
string input = Console.ReadLine();
if(string.Equals(input, "quit", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("You have quit the program");
break;
}
else if (match.Success)
{
Console.WriteLine(match.Value);//where is the match var
employees.Add(match.Value);//where is the match var
}
}
return employees;
}
}
public class Program
{
public static void Main()
{
Testing T1 = new Testing();
var employees = T1.Test1();
Iterating I1 = new Iterating();
I1.Test2(employees);
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have an array of string of .sql files directory that I need to sort in ascending order. Actually I want to sort it based on numeric part, if no numeric part then file should come on top.
How can I do this?
3.5.1_Patch
3.5_CoreScript
3.6_Patch
3.6.1_Patch
You should make you question as specific as possible. You can do what you want using Array.Sort and a lambda function:
string [] files = new string[] {"3.6.1_Patch","3.5_CoreScript","3.5.1_Patch","3.6_Patch"};
Array.Sort(files, (x,y) => {
string [] x1 = x.Split('_');
string [] y1 = y.Split('_');
return String.Compare(x1[0], y1[0]);
});
I left it to you to handle the edge case where there is no version number.
EDIT
Your file names are more complex than originally presented. I'll assume everything up to the first non-numeric string is the version?
At this point I would make a class to parse the filenames and store the version numbers. It also implements the IComparable interface for sorting.
public class Version : IComparable<Version> {
// Just guessing here - without knowing the actual format
public int Major = 0;
public int Minor = 0;
public int Build = 0;
public string FileName;
public Version(string fileName) {
ParseFileName(fileName);
}
// Split the string on '_' or '.',
// Considers the first 3 numbers to be version
// (stops parsing at non-numeric value)
public void ParseFileName(string fileName)
{
FileName = fileName;
string [] data = Regex.Split(fileName, #"[_\.]");
int x;
if (Int32.TryParse(data[0], out x)) {
Major = x;
if (2 <= data.Length && Int32.TryParse(data[1], out x)) {
Minor = x;
if (3 <= data.Length && Int32.TryParse(data[2], out x)) {
Build = x;
}
}
}
}
public override string ToString() {
return FileName;
}
// Comparison
public int CompareTo(Version v) {
int c = Major.CompareTo(v.Major);
if (0 == c) {
c = Minor.CompareTo(v.Minor);
}
if (0 == c) {
c = Build.CompareTo(v.Build);
}
return c;
}
}
Test program:
string [] files = new string[] {"10.6.1_Patch","3.5_CoreScript","3.5.1_Patch","3.6_Patch","10.6_Patch", "test", "01_1_ALTER_TC_EDB_V3", "01_2_ALTER_TC_EDB_V3"};
Version [] versions = new Version[files.Length];
for (int i = 0; i < files.Length; i++) {
versions[i] = new Version(files[i]);
}
Array.Sort(versions);
foreach (var v in versions) {
Console.WriteLine(v.ToString());
}
Output:
test
01_1_ALTER_TC_EDB_V3
01_2_ALTER_TC_EDB_V3
3.5_CoreScript
3.5.1_Patch
3.6_Patch
10.6_Patch
10.6.1_Patch
Try this :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication34
{
class Program
{
static void Main(string[] args)
{
List<string> input = new List<string>() { "3.5.1_Patch", "3.5_CoreScript", "3.6_Patch", "3.6.1_Patch" };
input.Sort((x,y) => new CustomSort() { s = x}.CompareTo(y));
}
}
public class CustomSort : IComparable
{
public string s { get; set; }
public int CompareTo(object input)
{
string[] thissplitArray = (s).Split(new char[] { '_' }).ToArray();
string[] splitArray = ((string)input).Split(new char[] { '_' }).ToArray();
if (thissplitArray[0] == splitArray[0])
{
return thissplitArray[1].CompareTo(splitArray[1]);
}
else
{
return thissplitArray[0].CompareTo(splitArray[0]);
}
}
}
}
I am making a Quiz/Trivia Game and have questions stored in XML file and it gets a random question. Everything works fine, but I want to stay random but not show the same question until every question has been shown.
public struct Question {
public string questionText;
public string answerA;
public string answerB;
public string answerC;
public string answerD;
public int correctAnswerID;
}
[XmlRoot("QuestionsRoot")]
public class QuestionData {
[XmlArray("Questions")]
[XmlArrayItem("Question")]
public List<Question>questions = new List<Question>();
public static QuestionData LoadFromText(string text) {
try {
XmlSerializer serializer = new XmlSerializer(typeof(QuestionData));
return serializer.Deserialize(new StringReader(text)) as QuestionData;
} catch (Exception e) {
UnityEngine.Debug.LogError("Exception loading question data: " + e);
return null;
}
}
And I use this in my other script to get a random question:
questionData = QuestionData.LoadFromText(questionDataXMLFile.text)
q = Random.Range(0, questionData.questions.Count);
currentQuestion = questionData.questions[q];
Do something along the following lines:
create another list with available questions - initialise it to the list of all questions:
questionData = QuestionData.LoadFromText(questionDataXMLFile.text)
var available = new List<Question>(questionData.question);
...
}
public Question GetNextQuestion()
{
if (available.Count == 0)
available.AddRange(questionData.question);
q = Random.Range(0, available.Count);
currentQuestion = available[q];
available.RemoveAt(q);
return currentQuestion;
}