My program reads from a text file delimited with a comma. The answers are an array of strings which I can't get to populate at the console.
The Question will show up, the correct Answer(a single char) will show up and the explanation will show up, it is just the array with the answers I am having problems with.
I will supply the code below.
The Question Unit( Structure of the Questions from the text file)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HoodTalkTrivia
{
class QuestionUnit
{
private string correctAnswer;
private string explanation;
private string question;
private string[] answer = new string[4];
public string CorrectAnswer
{
get { return correctAnswer; }
set { correctAnswer = value; }
}
public string Explanation
{
get { return explanation; }
set { explanation = value; }
}
public string Question
{
get { return question; }
set { question = value; }
}
public string[] Answer
{
get {return answer; }
set { answer = value; }
}
}
}
The Question Bank
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace HoodTalkTrivia
{
class QuestionBank
{
List<QuestionUnit> theQuestionsList = new List<QuestionUnit>();
// private string[] Questions;
const int NUM_ANSWERS = 4;
const int NUM_QUESTIONS = 5;
public int GetNumberOfAnswers
{
get { return NUM_ANSWERS; }
}
public int GetNumberOfQuestions
{
get { return NUM_QUESTIONS; }
}
public string GetCorrectAnswer(int index)
{
return theQuestionsList[index].CorrectAnswer;
}
public string GetExplanation(int index)
{
return theQuestionsList[index].Explanation;
}
public string GetQuestion(int index)
{
return theQuestionsList[index].Question;
}
public string[] GetAnswer(int index)
{
return theQuestionsList[index].Answer;
}
public bool ReadQuestionFile()
{
bool success = true;
FileInfo httSourceFile = new FileInfo("Questions.txt");
string line;
string[] fields;
char[] delimiter = { System.Convert.ToChar(",") };
QuestionUnit httQuestionUnit;
//int i = 0;
try
{
StreamReader httReader = httSourceFile.OpenText();
line = httReader.ReadLine();
while (line != null)
{
httQuestionUnit = new QuestionUnit();
fields = line.Split(delimiter);
httQuestionUnit.Question = fields[0];
string[] aArray = new string[4];
aArray[0] = fields[1];
aArray[1] = fields[2];
aArray[2] = fields[3];
aArray[3] = fields[4];
httQuestionUnit.Answer = aArray;
httQuestionUnit.CorrectAnswer = fields[5];
httQuestionUnit.Explanation = fields[6];
theQuestionsList.Add(httQuestionUnit);
line = httReader.ReadLine();
}
}
catch
{
success = false;
}
return success;
}
}
}
And the main
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HoodTalkTrivia
{
class HoodTalkTriviaGame
{
HoodTalkTriviaStrings myGameStrings;
public void PlayAgain()
{
}
public void Play()
{
QuestionBank myQuestions = new QuestionBank();
DisplayWelcome();
myQuestions.ReadQuestionFile();
for (int i = 0; i < myQuestions.GetNumberOfQuestions; i++)
{
Console.WriteLine(myQuestions.GetQuestion(i));
for (int j = 0; j < myQuestions.GetNumberOfAnswers; j++)
{
Console.WriteLine(myQuestions.GetAnswer(j));
}
// Console.WriteLine(myQuestions.GetCorrectAnswer(i));
Console.WriteLine("Make a selection, A - D");
Console.ReadLine();
}
}
public void DisplayWelcome()
{
myGameStrings = new HoodTalkTriviaStrings();
myGameStrings.WelcomeString();
}
public char PromptForGuess()
{
char guess = ' ';
return guess;
}
}
}
Here is an image of what i am getting at the console.
GetAnswer method returning an array of strings, so you are seeing the name of the type of your array in the console instead of the contents.
You can change your code like this:
Change this code:
for (int j = 0; j < myQuestions.GetNumberOfAnswers; j++)
{
Console.WriteLine(myQuestions.GetAnswer(j));
}
To:
var answers = myQuestions.GetAnswer(i);
for (int j = 0; j < answers.Length; j++)
{
Console.WriteLine(answers[j]);
}
Then you should see the answers of your question in the console.
You need to use the second index for the possible answers, because Answer is an array again:
for (int j = 0; j < myQuestions.GetNumberOfAnswers; j++)
{
Console.WriteLine(myQuestions.GetAnswer(i)[j]);
}
An alternative way of solving the problem is to change the acessor method:
public string GetAnswer(int questionIndex, int answerIndex)
{
return theQuestionsList[questionIndex].Answer[answerIndex];
}
And then the loop will be:
for (int j = 0; j < myQuestions.GetNumberOfAnswers; j++)
{
Console.WriteLine(myQuestions.GetAnswer(i, j));
}
Now the signature of the GetAnswer makes it impossible to forget the second index. It would also be helpful to rename the Answer property to Answers. This would remind you that an array is returned.
I think your solution a bit over-designed. An overall simplified approach would make it easier to use the classes. If the QuestionBank simply had a property returning an enumeration of question units this would be enough. No need for all the GetXYZ methods.
class QuestionBank
{
List<QuestionUnit> theQuestionsList = new List<QuestionUnit>();
public IEnumerable<QuestionUnit> Questions { get { return theQuestionsList; } }
...
}
Returning an IEnumerable<QuestionUnit> has the advantage over returning a List<QuestionUnit> that the real type of the question list is not made public, thus allowing you to change the implementation at any time without invalidating any code that accesses the questions from the outside.
Then the loops will become easier:
foreach (QuestionUnit questionUnit in myQuestions)
{
Console.WriteLine(questionUnit.Question);
foreach (string answer in questionUnit.Answers)
{
Console.WriteLine(answer);
}
Console.WriteLine("Make a selection, A - D");
Console.ReadLine();
}
Related
I have the user inputting a string which is stored in the variable 'word'. I now want to add this stored variable to the linked list. I've tried using
LinkedList<string>.Add(word);
To add the variable to the linked this but it isn't working and is returning the error "An object reference is required for the non-static field, method or property 'LinkedList.Add(string)"
I'm assuming it has something to do with my linked list but I'm not to sure.
Any help or ideas on the issue would be great.
using System;
using System.Collections.Generic;
using System.Text;
namespace project
{
public class LinkedList<TData>
{
private Node<TData> head;
private int count;
public LinkedList(string word)
{
this.head = null;
this.count = 0;
}
public bool Empty
{
get { return this.count == 0; }
}
public int Count
{
get { return this.count; }
}
public TData this[int index]
{
get { return this.Get(index); }
}
public TData Add(int index, TData data)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (index > count)
index = count;
Node<TData> current = this.head;
if (this.Empty || index == 0)
{
this.head = new Node<TData>(data, this.head);
}
else
{
for (int i = 0; i < index - 1; i++)
{
current = current.Next;
current.Next = new Node<TData>(data, current.Next);
}
}
count++;
return data;
}
public TData Add(TData data)
{
return this.Add(count, data);
}
public TData Remove(int index)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (this.Empty)
return default(TData);
if (index >= this.count)
index = count - 1;
Node<TData> current = this.head;
TData result;
if (index == 0)
{
result = current.Data;
this.head = current.Next;
}
else
{
for (int i = 0; index < index - 1; i++) ;
current = current.Next;
result = current.Next.Data;
current.Next = current.Next.Next;
}
count--;
return result;
}
public void Clear()
{
this.head = null;
this.count = 0;
}
public int IndexOf(TData data)
{
Node<TData> current = this.head;
for (int i = 0; i < this.count; i++)
{
if (current.Data.Equals(data))
return i;
current = current.Next;
}
return -1;
}
public bool Contains(TData data)
{
return this.IndexOf(data) >= 0;
}
public TData Get(int index)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (this.Empty)
return default(TData);
if (index >= this.count)
index = this.count - 1;
Node<TData> current = this.head;
for (int i = 0; i < index; i++)
current = current.Next;
return current.Data;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace project
{
public class Node<TData>
{
private Node<TData> next { get; set; }
public Node(TData data, Node<TData> next)
{
this.Data = data;
this.next = next;
}
public TData Data { get; set; }
public Node<TData> Next
{
get { return this.next; }
set { this.next = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace project
{
class Program
{
static void Main(string[] args)
{
string word;
Console.WriteLine("Please enter a word");
word = Console.ReadLine();
Console.WriteLine("You typed: " + word);
Console.ReadKey();
var list = new LinkedList<string>(word);
LinkedList<string>.Add(word);
}
}
}
Change this:
var list = new LinkedList<string>(word);
LinkedList<string>.Add(word);
To this:
var list = new LinkedList<string>();
list.Add(word);
list is your linked list object in memory. Whenever you want to do something with your list, you use the variable reference list, not the name for the kind of object it is. The reason we use the name of the variable rather than the name of the kind is because we might very well want to have two or more of them in our program:
var postiveList = new LinkedList<string>();
var negativeList = new LinkedList<string>();
positiveList.Add("happy");
negativeList.Add("sad");
It's only possible to use the name of the type(kind), when you're using something declared as static - and nothing in your program is static that I can point to and make a suitable demonstration of. Something you'll have probably used though:
int x = Convert.ToInt32("1234");
Convert here is static, the name of a type of class. You don't need to make a new Convert before you use it, and you can't have (nor need) more than one Convert in a program, so it makes sense to have it be static (only one of them)
Some things are both static and not:
var startTime = new DateTime(1970,1,1);
var endTime = DateTime.UtcNow;
startTime and endTime are both instances of a DateTime. To get the end time, however, we used a static property UtcNow of the DateTime type. It created a new DateTime for us based on the value of the computer clock and returned it. We didn't need for any other datetime to exist before we did this. If it helps, think of these static things as being like factories that churn out manufactured objects on demand. There is only one factory, and the things it makes are always the same kind of thing but it can make millions of them
Also change this:
public LinkedList(string word)
{
this.head = null;
this.count = 0;
}
To this:
public LinkedList()
{
this.head = null;
this.count = 0;
}
There's no point asking for something and then not doing anything with it - it just makes the thing that asks harder to use. I get the feeling you didn't write LinkedList, but you might have added this in while trying to get a word into it
you write:
LinkedList<string>.Add(word);
This is an attempt to call a static method Add on the type LinkedList<string>. And the compiler is telling you that this is a instance method and not a static method. I.e. you want to call:
list.Add(word);
Also, the constructor
public LinkedList(string word)
takes a string, but does not do anything with it. This parameter should probably be removed.
Good day,
I try that when I connect two documents, the numbered lists are reset. As in the example below.
I use the Xceed.Words.NET DocX libary.
In this Sample, the Template is :
Test Header
List:
1) <-This should be one
And the Result is:
Test Header
List:
1) <-This should be one
Test Header
List:
2) <-This should be one
Test Header
List:
3) <-This should be one
With the following code I am able to create the lists with similar formatting, but the formatting does not match 100%. Does anyone have any idea which way to try otherwise?
Note the number of existing paragraphs and call the function to reset the list after inserting the document.
/// <summary>
/// Insert complete WordFile
/// </summary>
/// <param name="wordFile"></param>
public void InsertWordTemplate(IWordFile wordFile, bool InsertPageBreak)
{
if (wordFile != null && wordFile.IsFileOk)
{
int pargraphCount = Document.Paragraphs.Count - 1;
// NotNeeded for this Problem wordFile.RemoveStyles();
// NotNeeded for this Problem RemoveHeaderAndFooter(wordFile);
Document.InsertDocument(wordFile.Document);
// NotNeeded for this Problem ReplaceSectionBreak(InsertPageBreak, pargraphCount);
ResetNumberedList(pargraphCount);
logger.Info("Word file inserted: " + wordFile.File.FullName);
}
else
{
logger.Warn("Word file is not okay - will not be inserted: " + wordFile?.File?.FullName);
}
}
In the Word document, three different names are used in a list, only from the 4th level is worked with a level. For other Word templates they are called different.
private void ResetNumberedList(int pargraphCount)
{
string styleName1 = "ListNumbers";
string styleName2 = "PIgeordneteListe2Ebene";
string styleName3 = "PIgeordneteListe3Ebene";
NumberedListReset numberedListReset = new NumberedListReset(Document, styleName1, styleName2, styleName3);
bool OnlyFirstFoundList = true;
numberedListReset.Reset(pargraphCount, OnlyFirstFoundList);
}
Below is the helper class with which I try to reset the numbering. I do this by myself
I notice the formatting of the individual list items, create new lists, fill them with the old values, set the styles correctly again and then insert everything into old place.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xceed.Document.NET;
using Xceed.Words.NET;
namespace PIB.Report.DataWriter.WordExport
{
public class NumberedListReset
{
private readonly DocX _Document;
private readonly string _StyleName1;
private readonly string _StyleName2;
private readonly string _StyleName3;
public NumberedListReset(DocX Document, string StyleName1, string StyleName2, string StyleName3)
{
_Document = Document;
_StyleName1 = StyleName1;
_StyleName2 = StyleName2;
_StyleName3 = StyleName3;
}
public void Reset(int StartParagraphNumber, bool OnlyFirstFinding)
{
for (int i = StartParagraphNumber; i < _Document.Paragraphs.Count; i++)
{
var paragraph = _Document.Paragraphs[i];
if (paragraph.IsListItem == true && paragraph.ListItemType == ListItemType.Numbered && paragraph.StyleName == _StyleName1)
{
//int? numId = GetNumId(paragraph);
//if (numId != -1)
//{
//}
ResetFoundList(ref i);
if (OnlyFirstFinding == true)
{
break;
}
}
}
}
private void ResetFoundList(ref int ParagraphCounter)
{
List<ParagraphMemorize> ParagraphMemorizes = CreateMemorizeListItems(ParagraphCounter);
if (ParagraphMemorizes.Count != 0)
{
RemoveOldParagraphsFromDocument(ParagraphMemorizes);
List numberedList = CreateNewDocumentList();
FillDocumentList(ParagraphMemorizes, numberedList);
List<Paragraph> actualListData = numberedList.Items;
ResetSyleNames(ParagraphMemorizes, actualListData);
InsertNewParagraphsToDocument(ParagraphCounter, actualListData);
ParagraphCounter += ParagraphMemorizes.Count;
}
}
private List<ParagraphMemorize> CreateMemorizeListItems(int ParagraphCounter)
{
List<ParagraphMemorize> ParagraphMemorizes = new List<ParagraphMemorize>();
for (int ii = ParagraphCounter; ii < _Document.Paragraphs.Count; ii++)
{
var paragraph = _Document.Paragraphs[ii];
if (!NameIsKnown(paragraph.StyleName))
{
break;
}
ParagraphMemorize paragraphMemorize = new ParagraphMemorize(paragraph);
paragraphMemorize.ListLevel = GetListLevel(paragraph);
ParagraphMemorizes.Add(paragraphMemorize);
}
return ParagraphMemorizes;
}
private void RemoveOldParagraphsFromDocument(List<ParagraphMemorize> ParagraphMemorizes)
{
ParagraphMemorizes.ForEach(m => _Document.RemoveParagraph(m.Paragraph));
}
private List CreateNewDocumentList()
{
return _Document.AddList(startNumber: 1);
}
private void FillDocumentList(List<ParagraphMemorize> ParagraphMemorizes, List numberedList)
{
for (var ii = 0; ii < ParagraphMemorizes.Count; ii++)
{
//numberedList.AddItem(ParagraphMemorizes[ii].Paragraph); //Raised an Error
ParagraphMemorize paragraphMemorize = ParagraphMemorizes[ii];
int listLevel = GetListLevel(paragraphMemorize);
_Document.AddListItem(numberedList, paragraphMemorize.Text, listLevel);
}
}
private static void ResetSyleNames(List<ParagraphMemorize> ParagraphMemorizes, List<Paragraph> actualListData)
{
for (int ii = 0; ii < actualListData.Count; ii++)
{
actualListData[ii].StyleName = ParagraphMemorizes[ii].StyleName;
}
}
private void InsertNewParagraphsToDocument(int i, List<Paragraph> actualListData)
{
Paragraph paragraph = _Document.Paragraphs[i];
for (int ii = 0; ii < actualListData.Count; ii++)
{
paragraph.InsertParagraphBeforeSelf(actualListData[ii]);
}
}
private bool NameIsKnown(string Name)
{
return Name == _StyleName1 | Name == _StyleName2 | Name == _StyleName3;
}
private int GetListLevel(ParagraphMemorize paragraphMemorize)
{
if (paragraphMemorize.StyleName == _StyleName1)
{
return 0;
}
else if (paragraphMemorize.StyleName == _StyleName2)
{
return 1;
}
else if (paragraphMemorize.StyleName == _StyleName3)
{
return (int)paragraphMemorize.ListLevel;
}
else
{
return 0;
}
}
private int? GetNumId(Paragraph paragraph)
{
var numIds = paragraph.ParagraphNumberProperties.Descendants().Where(e => e.Name.LocalName.Equals("numId"));
foreach (var numId in numIds)
{
XNamespace nsW = Namespace.WordNamespace;
var values = numId.Attributes(XName.Get("val", nsW.ToString()));
foreach (var value in values)
{
int resultId = 0;
int.TryParse(value.Value, out resultId);
return resultId;
}
}
return null;
}
private int? GetListLevel(Paragraph paragraph)
{
var numIds = paragraph.ParagraphNumberProperties.Descendants().Where(e => e.Name.LocalName.Equals("ilvl"));
foreach (var numId in numIds)
{
XNamespace nsW = Namespace.WordNamespace;
var values = numId.Attributes(XName.Get("val", nsW.ToString()));
foreach (var value in values)
{
int resultId = 0;
int.TryParse(value.Value, out resultId);
return resultId;
}
}
return null;
}
private class ParagraphMemorize
{
public Paragraph Paragraph { get; set; }
public string Text { get; set; }
public string StyleName { get; set; }
public int? ListLevel { get; set; }
public ParagraphMemorize(Paragraph Paragraph)
{
this.Paragraph = Paragraph;
this.Text = Paragraph.Text;
this.StyleName = Paragraph.StyleName;
}
}
}
}
I'm trying to extend C# List to use a Print function. Using Console.WriteLine(a.Print()) only shows partial output but a.Print2() work correctly where in a.Print I'm calling a method with return type string and in a.Print2 I'm calling a function with return value void.
using System;
using ExtensionMethods;
using System.Collections.Generic;
namespace ExtensionMethods
{
public static class ExtensionClass
{
public static int PlusFive(this int input)
{
return input + 5;
}
static public string Print(this List<int> input)
{
int i;
string output = "";
for (i = 0; i < input.Count - 1; i++)
{
output = input[i].ToString() + ", ";
}
output += input[i].ToString();
return output;
// Outputs 2, 3
}
static public void Print2(this List<int> input)
{
int i;
for (i = 0; i < input.Count - 1; i++)
{
Console.Write(input[i].ToString() + ", ");
}
Console.Write(input[i].ToString());
// Outputs 1, 2 ,3
}
}
}
namespace LearningCSharp
{
class Program
{
static void Main(string[] args)
{
List<int> a = new List<int>(){1, 2, 3};
// int b = 2;
Console.WriteLine(a.Print());
a.Print2();
}
}
}
Because your Print() method is broken. In the loop you always set output to a new string. You're missing the plus:
static public string Print(this List<int> input)
{
int i;
string output = "";
for (i = 0; i < input.Count - 1; i++)
{
// missing plus here
output += input[i].ToString() + ", ";
}
return output;
}
Note however that there are better possibilites to concat strings. One solution here could be:
static public string Print(this List<int> input)
{
return string.Join(", ", input);
}
I've got the following Problem:
I want to take parts of a string into an array. So far no problem (splitstring),
but, I can't use splitstring because it takes my operators out.
Explained on an example:
Following string: "47-62*5141"
I need it like this: {"47", "-", "62", "*", "5141"}
If you could give me a code example, I would be very pleased!
Just split according to the word boundary which exist at the middle.
Regex.Split(string, #"(?!^)\b(?!$)");
DEMO
I've made a little uglycode. But it works.
class Program
{
static void Main(string[] args)
{
var text = "47-62**5141";
var splittedText = text.SplitAndKeepSeparator('-', '*');
foreach (var part in splittedText)
{
Console.WriteLine(part);
}
Console.ReadLine();
}
}
public static class StringExtensions
{
public static IEnumerable<string> SplitAndKeepSeparator(this string s, params char[] seperators)
{
var parts = s.Split(seperators, StringSplitOptions.None);
var partIndex = 0;
var isPart = true;
var indexInText = 0;
while (partIndex < parts.Length)
{
if (isPart)
{
var partToReturn = parts[partIndex];
if (string.IsNullOrEmpty(partToReturn))
{
partToReturn = s[indexInText].ToString();
}
else
{
isPart = false;
}
indexInText += partToReturn.Length;
partIndex++;
yield return partToReturn;
}
else
{
var currentSeparator = s[indexInText];
indexInText++;
isPart = true;
yield return currentSeparator.ToString();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] parts = new string[100];
var text = "47-62*5141";
int i = 0;
var splittedText = text.SplitAndKeepSeparator('-', '*', '+', '/');
foreach (var part in splittedText)
{
parts[i] = part;
i++;
}
Console.ReadLine();
Console.WriteLine(parts[0]);
Console.WriteLine(parts[1]);
Console.WriteLine(parts[2]);
Console.WriteLine(parts[3]);
Console.ReadLine();
}
}
}
public static class StringExtensions
{
public static IEnumerable<string> SplitAndKeepSeparator(this string s, params char[] seperators)
{
var parts = s.Split(seperators, StringSplitOptions.None);
var partIndex = 0;
var isPart = true;
var indexInText = 0;
while (partIndex < parts.Length)
{
if (isPart)
{
var partToReturn = parts[partIndex];
if (string.IsNullOrEmpty(partToReturn))
{
partToReturn = s[indexInText].ToString();
}
else
{
isPart = false;
}
indexInText += partToReturn.Length;
partIndex++;
yield return partToReturn;
}
else
{
var currentSeparator = s[indexInText];
indexInText++;
isPart = true;
yield return currentSeparator.ToString();
}
}
}
}
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.