C# two-dimensional array to validate entry in the form - c#

There is a form that each user is required to complete, it has 4 fields: date, bill code, amount and currency. Bill code has a drop down menu with a lot of options of 4 that options are valid (Health, Travel, Meal, Hotel). Bill code field cannot be left blank, and it should take only one of these 4 options. A user make 4 entries with each of the 4 bill codes. If user enters only Health and Travel, an error message should fire that Meal and Hotel records need to be added. This is what I got so far:
public bool ValidateBillCode(bills billArray[][])
{
for(int i = 0; i < billArray.Length; i++)
{
for(int j = 0; j < billArray[0].Length; j++)
{
if(billArray[i][j].IndexOf("Health") >= 0 ||
billArray[i][j].IndexOf("Travel") >= 0 ||
billArray[i][j].IndexOf("Meal") >= 0||
billArray[i][j].IndexOf("Hotel") >= 0)
{
return true;
}
else
{
return false;
}
}
}
}
But it doesn't make sure that all four of these are entered, and I'm not sure how to make an error message that would tell the user which of the four are missing. I will appreciate any help with this.

I believe you don't need any 2-dimensional or jagged array. You need to define a struct like:
public struct Bill
{
public string Date;
public string BillCode;
public string Amount;
public string Currency;
}
Then your ValidateBillCode() becomes like this:
public bool ValidateBillCode(Bill[] billArray)
{
bool healthEntered = false;
bool travelEntered = false;
bool mealEntered = false;
bool hotelEntered = false;
for (int i = 0; i < billArray.Length; i++)
{
if (billArray[i].BillCode == "Health")
healthEntered = true;
else if (billArray[i].BillCode == "Travel")
travelEntered = true;
else if (billArray[i].BillCode == "Meal")
mealEntered = true;
else if (billArray[i].BillCode == "Hotel")
hotelEntered = true;
}
return healthEntered && travelEntered && mealEntered && hotelEntered;
}
But this is just a very simplistic approach. For a more appropriate solution, you'd better to use class instead of struct, use enum for BillCode, DateTime for Date, and double for Amount.

Related

Array value is set to zero after the whole process generated by user

I want to create a minigame which select random buttons from an array of game objects and store the value in an array. After the first step is completed the user need to tap on the same buttons or he will lose. The problem is when I want to compare the values from this two arrays, every value from index 0-2 is set to 0 in both arrays. I tried to debug the adding part and that works fine, even in my editor I can see the stored values. Here is a photo:
storedobjectsEditor. I even put two for loops to check the values from array in the game, instead of getting 3 prints I get 6 of them, first 3 represent the values right and the other 3 have value = 0 (this apply to both arrays). In my CheckWin() the result will be always true because the values which are compared there are 0 for every position from both arrays. I can't figure it out what force the arrays to have all components set to zero in that method. Here is the script:
public class Script : MonoBehaviour
{
[SerializeField] private GameObject[] buttons;
[SerializeField] private int[] culoriINT;
[SerializeField] private int[] culoriComparareINT;
int index = 0;
int index2 = 0;
private bool win = false;
private void Start()
{
StartCoroutine(ChangeColors2());
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
for (int i = 0; i < culoriINT.Length; i++)
{
Debug.Log("INT vector[" + i + "]: " + culoriINT[i]);
}
}
if (Input.GetKeyDown(KeyCode.W))
{
for (int i = 0; i < culoriComparareINT.Length; i++)
{
Debug.Log("Al doilea vector[" + i + "]: " + culoriComparareINT[i]);
}
}
}
IEnumerator ChangeColors2()
{
yield return new WaitForSeconds(1f);
for (int j = 0; j < buttons.Length; j++)
{
var randomBtn = UnityEngine.Random.Range(0, buttons.Length);
buttons[randomBtn].GetComponent<Image>().color = Color.green;
var introducereIndex = buttons[randomBtn].GetComponent<IndexButtons>().index;
culoriINT[index] = introducereIndex;
Debug.Log($"Index adaugat {introducereIndex} total {culoriINT.Length}");
index++;
yield return new WaitForSeconds(0.5f); //seteaza coloare alb pe acelas buton in cazu in care nimereste acelas sa se vada
buttons[randomBtn].GetComponent<Image>().color = Color.white;
Debug.Log("verde");
yield return new WaitForSeconds(1f);
}
}
public void OnButtonClick()
{
index2++;
}
public void numePeClick()
{
if (index2 < buttons.Length)
{
string a = EventSystem.current.currentSelectedGameObject.name;
culoriComparareINT[index2] = Convert.ToInt32(a);
Debug.Log($"Index adaugat {Convert.ToInt32(a)} total {culoriComparareINT.Length}");
}
else
{
Debug.Log("Array plin");
}
}
public void CheckWin()
{
win = true;
for (var i = 0; i < culoriINT.Length; i++)
{
if (culoriINT[i] != culoriComparareINT[i])
{
win = false;
break;
}
else
{
win = true;
}
}
if (win)
{
Debug.Log("Ai castigat!");
}
else
{
Debug.Log("Ai pierdut!");
}
}
}
It sounds like you have two instances of the same Script component in your scene. One of them has the correct values 3,2,3, the other one has the wrong values 0,0,0. This is why six values get printed to the console instead of three with a single input.

Writing a program in C# where depending on the Id (txtBxNumber) it will either update or create a new record in a text file and a Rich Text Box

fileName = txtBxFileNamePath.Text;
if (File.Exists(fileName))
{
if (txtBxDate.Text != null && txtBxNumber.Text != null && txtBxUnit.Text != null && txtBxUnitPrice.Text != null && txtBxShipTo.Text != null
&& txtBxOrdered.Text != null && richTxBxDesc.Text != null)
{
try
{
int higherThanZero = Int32.Parse(txtBxNumber.Text);
if (higherThanZero > 0)
{
using (StreamReader reader = File.OpenText(fileName))
{
string[] lines = File.ReadAllLines(fileName);
for (int i = 0; i < lines.Length - 1; i++)
{
string firstNum = lines[i].Substring(0, 2);
if (firstNum == txtBxNumber.Text)
{
string record = "hello ";
lines[i].Replace(lines[i], record);
}
else
{
int orderNum = Int32.Parse(txtBxOrdered.Text);
int unitPriceNum = Int32.Parse(txtBxUnitPrice.Text);
double tax = .13;
int taxInt = (int)tax;
int amount = orderNum * unitPriceNum;
string amountStr = amount.ToString();
int amountTotal = amount * taxInt;
string amountTotalStr = amountTotal.ToString();
amountList.Add(amountStr);
amountTotalList.Add(amountTotalStr);
string record = amountTotalStr.PadRight(30) + amountStr.PadRight(30);
richTxtBxRecord.Text += record + "\n";
using (StreamWriter write = new StreamWriter(fileName, true))
{
write.WriteLine(record + "\n");
write.Close();
}
}
}
}
}
else
{
richTxtBxError.Text += "Textbox Number must contain a digit higher than 0 ";
}
}
catch
{
richTxtBxError.Text += "Please make sure number text box is a digit";
}
}
else
{
richTxtBxError.Text += "please make sure that no text boxes are empty";
}
}
else
{
richTxtBxError.Text += "Please select a file that already exists";
}
I am having an issue where once i get past the try-catch statement "please make sure number is a digit, no code executes. I am trying to obtain the first few characters in a text file and match it with the users input. If the input is the same as what is already inserted in the text file, i update the whole record. If there is no match (non existent number) i write in a brand new record.
I can't quite follow your logic, but I tried. You should be able to take this code and do what you want (whatever it is).
I started by declaring some class level variables.
private DateTime _dateValue;
private int _numberValue;
private decimal _unitPrice;
private int _numberOrdered;
Then, since you have so many preconditions and so many text boxes, I factored out the validation and setting of these variables. It makes the logic (whatever it supposed to be) much easier to follow:
private bool ValidateUserEntry()
{
bool isError = false;
if (!File.Exists(txtBxFileNamePath.Text))
{
AddError("File Name must exist");
isError = true;
}
if (txtBxDate.Text == string.Empty || !DateTime.TryParse(txtBxDate.Text, out var _dateValue))
{
AddError("The date must be a valid date");
isError = true;
}
if (txtBxNumber.Text == string.Empty || !int.TryParse(txtBxNumber.Text, out _numberValue) ||
_numberValue <= 0)
{
AddError("You must enter a number greater than 0 for [Number]");
isError = true;
}
if (txtBxUnitPrice.Text == string.Empty || !decimal.TryParse(txtBxUnitPrice.Text, out _unitPrice) ||
_unitPrice <= 0.0m)
{
AddError("The unit price must be a positive decimal number");
isError = true;
}
if (txtBxShipTo.Text == string.Empty)
{
AddError("A ship to address is required");
isError = true;
}
if (txtBxOrdered.Text == string.Empty || !int.TryParse(txtBxOrdered.Text, out _numberOrdered) ||
_numberOrdered <= 0)
{
AddError("The Number ordered must be a number greater than 0");
isError = true;
}
if (richTxBxDesc.Text == string.Empty)
{
AddError("A description is required");
isError = true;
}
return !isError;
}
I also added two utility functions for managing the error list:
private void ClearError()
{
richTxtBxError.Text = string.Empty;
}
private void AddError(string errorMessage)
{
richTxtBxError.Text += (errorMessage + Environment.NewLine);
richTxtBxError.SelectionStart = richTxtBxError.Text.Length;
richTxtBxError.SelectionLength = 0;
}
Now comes the real code. Near as I can tell, you want to scan a text file. If the number in the first few character positions matches a number in your input, then you change the line to some constant text. Otherwise, you want to do a calculation and put the results of the calculation on the line of text.
My input file looks like this:
1 First
2 Second
3 Third
12 Twelth
13 Thirteenth
34 Thirty-fourth
and the code that I run looks like what's below. The logic makes no sense, but it was what I could discern from your code. Instead of trying to do things on the fly to a file (which never really turns out well unless you are really careful), I gather the output into a List<string>. Once I have all the output, I put it in a text box control and overwrite the file.
ClearError();
//check pre-conditions
if (!ValidateUserEntry())
{
return;
}
string[] lines;
using (StreamReader reader = File.OpenText(txtBxFileNamePath.Text))
{
lines = File.ReadAllLines(txtBxFileNamePath.Text);
}
List<string> newLines = new List<string>();
for (var lineIndex = 0; lineIndex < lines.Length; ++lineIndex)
{
var line = lines[lineIndex];
if (line.Length > 2 && int.TryParse(line.Substring(0, 2), out var linePrefixNumber) &&
linePrefixNumber == _numberValue)
{
newLines.Add("Bingo, hit the right record");
}
else
{
decimal tax = .13m;
var amount = _numberOrdered * _unitPrice;
var amountTotal = amount * (1m + tax);
//amountList.Add(amount.TosString());
//amountTotalList.Add(amountTotal.ToString());
var newRecord = $"{amountTotal,30:C}{amount,30:C}";
newLines.Add(newRecord); //every record but one will be the same, but, such is life
}
}
//at this point, the newLines list has what I want
//put it in the text box
richTxtBxRecord.Text = string.Join(Environment.NewLine, newLines);
//and write it out
using (StreamWriter write = new StreamWriter(txtBxFileNamePath.Text, append:false))
{
write.Write(richTxtBxRecord.Text);
write.Flush();
}
With inputs that look like:
Number: 12
Number Ordered: 3
Unit Price: 1.23
The output (oddly enough - but it's what I could figure from your code) looks like:
$4.17 $3.69
$4.17 $3.69
$4.17 $3.69
Bingo, hit the right record
$4.17 $3.69
$4.17 $3.69
You can see that the input line that had the 12 at the start gets switched for bingo. The rest get the same information. I'm sure that's not what you want. But, with this code, you should be able to get something that you'd like.
Also note that I treat all the currency values as decimal (not int or double). For the life of me, I have no idea what you were trying to do with the taxInt variable (it will always be zero the way you have coded it). Instead, I did a rational tax calculation.
All of the code below the catch block is inside an else block, so I wouldn't expect it to execute. If you want something to execute after the catch, remove it from the else block.

Are 3 or more times overlapping C#

I am trying to determine whether times ranges are overlapping on a CRUD page but am stuck.
I can get it working with two time ranges using the code below but I need it working for 3 as well.
public static bool IsOverLapping(ConfigureViewModel viewModel)
{
bool status = false;
var times = viewModel.Periods.OrderBy(x => x.StartTime.TimeOfDay).ToList();
for (var i = 0; i <= times.Count - 2; i++)
{
if (times[i].StartTime.TimeOfDay <= times[i + 1].EndTime.TimeOfDay)
{
if (times[i + 1].StartTime.TimeOfDay >= times[i].EndTime.TimeOfDay)
status = false;
else
return true;
}
else
return true;
}
return status;
}
The data comes in as DateTime values which is why I have only looked at the 'TimeOfDay' value. The image shows the layout of the CRUD page.
This is actually trickier than it seems, as you need to handle time periods that wrap across midnight.
Using some extension methods, you can make it straight forward.
First, determine if a time is between two others:
public static bool Between(this TimeSpan aTime, TimeSpan startTime, TimeSpan endTime) => (startTime <= endTime) ? (startTime < aTime && aTime < endTime)
: (startTime < aTime || aTime < endTime);
Then create a special version using the Period class for the range:
public static bool Between(this TimeSpan aTime, Period aPeriod) => aTime.Between(aPeriod.StartTime.TimeOfDay, aPeriod.EndTime.TimeOfDay);
Finally create a test for if one range overlaps a second range (note this is asymmetric):
public static bool Overlaps(this Period aPeriod, Period bPeriod) => aPeriod.StartTime.TimeOfDay.Between(bPeriod) || aPeriod.EndTime.TimeOfDay.Between(bPeriod);
Now go through all the ranges and check if any range overlaps another range:
public static bool IsOverLapping(this List<Period> periods) {
var periodCount = periods.Count;
for (int j1 = 0; j1 < periodCount; ++j1)
for (int j2 = 0; j2 < periodCount; ++j2)
if (j1 != j2 && periods[j1].Overlaps(periods[j2]))
return true;
return false;
}
Finally you can use the method in your ConfigureViewModel method:
public static bool IsOverLapping(ConfigureViewModel viewModel)
{
bool status = false;
var times = viewModel.Periods.OrderBy(x => x.StartTime.TimeOfDay).ToList();
return times.IsOverLapping();
}
I think it might be simpler than it sounds. If you have period1 and period2, they are NOT overlapping if period1.Start > period2.End or if period1.End < period2.Start. If neither of these are true, then we know that they are overlapping:
I made this a static method on the Period class:
public class Period
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public static bool AreOverlapping(Period first, Period second)
{
if (first == null || second == null) return false;
// These two conditions define "overlapping" and must be true
return first.StartTime <= second.EndTime &&
first.EndTime >= second.StartTime;
}
}
Then this should simplify the logic in your method that detects if there are any overlapping periods in a group:
public static bool DoAnyOverlap(List<Period> periods)
{
if (periods == null || periods.Count < 2) return false;
var ordered = periods.OrderBy(p => p.StartTime).ToList();
for (var i = 0; i < ordered.Count - 1; i++)
{
if (Period.AreOverlapping(ordered[i], ordered[i + 1])) return true;
}
return false;
}
If for some reason you cannot modify the Period class, the logic can easily be incorporated into the DoAnyOverlap method:
public static bool DoAnyOverlap(List<Period> periods)
{
if (periods == null || periods.Count < 2) return false;
var ordered = periods.Where(p => p != null).OrderBy(p => p.StartTime).ToList();
for (var i = 0; i < ordered.Count - 1; i++)
{
if (ordered[i].StartTime <= ordered[i + 1].EndTime &&
ordered[i].EndTime >= ordered[i + 1].StartTime)
{
return true;
}
}
return false;
}
Try this:
var periods = new[]
{
new { start = TimeSpan.Parse("10:00"), end = TimeSpan.Parse("14:00") },
new { start = TimeSpan.Parse("16:00"), end = TimeSpan.Parse("17:00") },
new { start = TimeSpan.Parse("13:00"), end = TimeSpan.Parse("15:00") },
};
bool overlapping =
Enumerable
.Range(0, periods.Length)
.SelectMany(i =>
Enumerable
.Range(i + 1, periods.Length - i - 1),
(i, j) => new { A = periods[i], B = periods[j] })
.Any(x => !(x.B.start >= x.A.end || x.B.end <= x.A.start));
It'll work with DateTime too.
Assuming ConfigureViewModel class looks like that:
class ConfigureViewModel
{
...
public List<Period> Periods { get; set; }
}
class Period
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
You can use Time Period Library for .NET:
PM> Install-Package TimePeriodLibrary.NET
And IsOverLapping mathod may look as simple as that:
public static bool IsOverLapping(ConfigureViewModel vm, int numberOfOverlaps)
{
var ranges = vm.Periods.Select(q => new TimeRange(q.StartTime, q.EndTime));
TimePeriodCollection periodCollection = new TimePeriodCollection(ranges);
TimePeriodIntersector<TimeRange> intersector = new TimePeriodIntersector<TimeRange>();
return intersector.IntersectPeriods(periodCollection).Count > numberOfOverlaps;
}

Trying to select from a list the first previous eligible index

I have a List<List<Object>> in which one of these objects has the property isCurrentlySelected = true. This nested list represents a grid of these objects that might be constructed in any configuration (i.e. the grid may be any dimensions, and may even be jagged).
Now, some of these objects have the property isSelectable = false, so I'm trying to create a method which takes a single parameter for "direction" and returns the first eligible index in the specified direction from the one currently marked as selected.
So far I've only been able to do this using several nested for loops and if-else statements, and even then, it will only work for one direction. I'm wondering if there's a more elegant way to check for the first eligible element, preferably using a single method.
Thanks in advance for any help,
~KWiP
EDIT: with code sample
So I have the Menu class:
public class Menu
{
public int x;
public int y;
public int width;
public int height;
public int lineHeight;
public float menuTimer = 0.0f;
public bool menuKeysPressed = false;
public List<List<Selection>> selections = new List<List<Selection>>();
public int selectedList = 0;
public int selectedItem = 0;
in which is the child class Selection:
public class Selection
{
public int x;
public int y;
public bool isCurrentlySelected = false;
public bool isSelectable = true;
public Color selectedColor = Color.White;
public Color unselectedColor = Color.Gray;
public bool isNonText = false;
public string displayText = "";
public int textSize;
public Selection(int xPos, int yPos, string text, int size, bool selectable)
{
x = xPos;
y = yPos;
displayText = text;
textSize = size;
if (!selectable)
{
isCurrentlySelected = false;
}
}
}
Each Selection in a Menu object has X and Y coordinates in the form of its indices in the nested lists found in Menu.selections. In each Menu, there is typically exactly one Selection with its isCurrentlySelected property set to true.
public Menu(int xPos, int yPos, int wdth, int hght, int lists = 0, int items = 0, bool allSelectable = true)
{
x = xPos;
y = yPos;
width = wdth;
height = hght;
if (items > 0 && lists <= 0)
{
lists = 1;
}
for (int i = 0; i < lists; i++)
{
selections.Add(new List<Selection>());
for (int j = 0; j < items; j++)
{
selections.ElementAt(i).Add(new Selection(((wdth / items) * j) + xPos, ((hght / lists) * i) + yPos, "", 20, allSelectable));
}
}
if (items > 0)
{
selections.ElementAt(0).ElementAt(0).isCurrentlySelected = true;
}
}
Now, within the Menu class, I'm trying to make a method which will deselect the currently selected index, and then select the next closest eligible index in a particular direction, wrapping around to the other side if the end of the range of indices is reached. Unfortunately, all I've been able to come up with is this mess, which currently only works going North, and would need to be expanded to roughly 4x its size to accommodate all directions.
public void nav(Point currentSelected, int dir) // The 4 cardinal directions are represented by an int: 0 for North and continuing clockwise from there.
{
int newRow = currentSelected.Y;
int newIndex = currentSelected.X;
switch (dir)
{
case 0: // Operations to select next eligible N index.
if (this.selections.Count <= 1)
{
return;
}
else
{
int firstOpenRow = this.selections.Count;
for (int i = 0; i < this.selections.Count; i++)
{
int difference = i - currentSelected.Y;
if (difference > 0 && difference < firstOpenRow && this.selections.ElementAt(i).ElementAt(currentSelected.X).isSelectable == true)
{
firstOpenRow = i;
}
}
if (firstOpenRow == this.selections.Count)
{
for (int i = 0; i < this.selections.Count; i++)
{
int difference = i - currentSelected.Y;
if (difference < 0 && difference < firstOpenRow && this.selections.ElementAt(i).ElementAt(currentSelected.X).isSelectable == true)
{
firstOpenRow = i;
}
}
if (firstOpenRow == this.selections.Count)
{
firstOpenRow = currentSelected.Y;
}
}
this.selections.ElementAt(currentSelected.Y).ElementAt(currentSelected.X).isCurrentlySelected = false;
this.selections.ElementAt(firstOpenRow).ElementAt(currentSelected.X).isCurrentlySelected = true;
}
break;
case 1:
// Add operations for E here
break;
case 2:
// Add operations for S here
break;
case 3:
// Add operations for W here
break;
}
}
Your choice of data-structure isn't really well made for the kind of queries you need to make. Maybe you should consider investing a little more time into creating a linked grid. Something like:
class Item
{
Item North { get; private set; }
Item South { get; private set; }
Item West { get; private set; }
Item East { get; private set; }
}
Like that, if you look for the next 'left' item, you can just go West until you either reach null or you encounter an item that is selectable.
Otherwise, it makes sense to distinguish between the different kinds of directions. Just because you have less code doesn't mean your program runs faster. Think of your algorithm in terms of how many operations you have to make to reach your goal, instead of how long it is.
Sorry if that's not the answer your're looking for.

if text box array how to add to listbox

I'm having a slight issue with my program. I'm a first year student and am trying to make a program on flight reservation.
if (txtSeat.Text == "1")
{
btnRowOneSeatOne.BackColor = Color.Red;
goodMessage += "You Selected Row One and Seat One";
}
So, this basically goes down for all 15 seats in the same manner. I have a textbox and if you type numbers from 1-15 the seat will button turn red.
I also did pretty much the same thing in removing the customer. As followed
This was in the remove button handle
if (txtRemove.Text == "1")
{
btnRowOneSeatOne.BackColor = Color.Black;
}
I haven't started on an array yet. Basically for that I have a text box once the number is enter it should store it in the array.
I believe this should be the coding
seatNum = int.Parse(txtSeat.Text);
bool availability = false;
for (int s = 0; s < seats.Length; s++)
{
seats[s] = seatNum
availability = true;
break;
}
if (availability == true)
{
}
I believe this should store the seat in array. I've tried many different ways, but couldn't seem to find away. This is an assignment for a class FYI.
int seatnum = int.Parse(textBox1.Text);
Seat = new int[15];
if (textBox1.Text != "")
{
Seat[seatnum - 1] = seatnum;
}
for (int i = 0; i < 15; i++)
{
if (Seat[i] != 0)
{
listBox1.Items.Add(textBox1.Text);
}
}

Categories

Resources