Printing out array as a table c# - c#

I am have trouble getting my program to print out an array of Employee class objects neat and orderly. I will proved my code, could someone please let me know how I would structure my write statement to make this happen.
What the output actually is:
Bruce Wayne, 123456, 25.88, 35.50
Clark Kent 232344 25.88 38.75
Diana Prince 657659 27.62 30.25
Hal Jordan 989431 23.14 44.25
Barry Allan 342562 25.12 25.50
Arthur Curry 565603 21.09 23.75
John Jones 812984 18.99 32.75
Dinah Lance 342988 18.99 34.50
Oliver Queen 340236 17.45 41.25
Ray Palmer 120985 24.75 40.00
Ronald Raymond 239824 16.43 35.00
Carter Hall 657123 19.34 42.75
Shayera Hol 761742 16.73 38.50
What the output should be:
Bruce Wayne 123456 25.88 35.50
Clark Kent 232344 25.88 38.75
Diana Prince 657659 27.62 30.25
Hal Jordan 989431 23.14 44.25
Barry Allan 342562 25.12 25.50
Arthur Curry 565603 21.09 23.75
John Jones 812984 18.99 32.75
Dinah Lance 342988 18.99 34.50
Oliver Queen 340236 17.45 41.25
Ray Palmer 120985 24.75 40.00
Ronald Raymond 239824 16.43 35.00
Carter Hall 657123 19.34 42.75
Shayera Hol 761742 16.73 38.50
// This is from the Employee class.
public void PrintEmployee()
{
Console.WriteLine(name + " " + number + " " + rate + " " + hours + "
" + gross);
}
// This is from main program.
for (int i = 0; i < Employees.Length; i++)
{
if (Employees[i] == null)
{
break;
}
Employees[i].PrintEmployee();
}
If you need more code please let me know.
Also if anyone knows a nicer way of printing the table please let me know.
All help is greatly appreciated.

If you look into the String.PadRight() method, you will see that it pads a string with whitespace characters until it is a specified width. We can do this with the name field, choosing some reasonable maximum name length (perhaps 25?) to size the column.
Also, instead of a PrintEmployee method, you might consider overwriting the ToString() method on your Employee class, so the default of employee.ToString() will look the way you want. For example:
public class Employee
{
public string Name { get; set; }
public int Number { get; set; }
public double Rate { get; set; }
public double Hours { get; set; }
public override string ToString()
{
return $"{Name.PadRight(20)} {Number} {Rate:00.00} {Hours:00.00}";
}
}
Now that we have the names displayed in a 20-character column (and the doubles are displayed with proper decimal places), things should line up well:
static void Main()
{
var employees = new List<Employee>
{
new Employee {Name = "Bruce Wayne", Number = 123456, Rate = 25.88, Hours = 35.5},
new Employee {Name = "Clark Kent", Number = 232344, Rate = 25.88, Hours = 38.75},
new Employee {Name = "Diana Prince", Number = 657659, Rate = 27.62, Hours = 30.25},
new Employee {Name = "Hal Jordan", Number = 989431, Rate = 23.14, Hours = 44.25},
new Employee {Name = "Barry Allan", Number = 342562, Rate = 25.12, Hours = 25.50},
new Employee {Name = "Arthur Curry", Number = 565603, Rate = 21.09, Hours = 23.75},
new Employee {Name = "John Jones", Number = 812984, Rate = 18.99, Hours = 32.75},
new Employee {Name = "Dinah Lance", Number = 342988, Rate = 18.99, Hours = 34.50},
new Employee {Name = "Oliver Queen", Number = 340236, Rate = 17.45, Hours = 41.25},
new Employee {Name = "Ray Palmer", Number = 120985, Rate = 24.75, Hours = 40.00},
new Employee {Name = "Ronald Raymond", Number = 239824, Rate = 16.43, Hours = 35.00},
new Employee {Name = "Carter Hall", Number = 657123, Rate = 19.34, Hours = 42.75},
new Employee {Name = "Shayera Hol", Number = 761742, Rate = 16.73, Hours = 38.50},
};
foreach (var employee in employees)
{
Console.WriteLine(employee);
}
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output

Have a look at this post here:
Format Strings in Console.WriteLine method
Padding will definitely be your answer. Depending on your needs, you can pad them at the Employee Class level on the getter in your properties.
Or do something like this:
var name = "Ronald Raymond";
var number = "239824";
var rate = "16.43";
var hours = "35.00";
var gross = "1256";
var formatted = string.Format("{0,-15}", name) +
string.Format("{0,-7}", number) +
string.Format("{0,-7}", rate) +
string.Format("{0,-7}", hours) +
string.Format("{0,-7}", gross);
Console.WriteLine(formatted);

For the fun of programming, you could also do this:
String FixStringLength(String input, int length) {
Char[] temp = new Char[length];
for(int i = 0; i < length; i++){
if(i < input.Length){
temp[i] = input[i];
}
else{
temp[i] = ' ';
}
}
return new String(temp);
}
And then
Console.WriteLine(FixStringLength(name, 30) + number);

Related

C# append text to a certain line

I try to build some table inside a text file which
look like this:
Name Grade
--------------------
John 100
Mike 94
...
...
I have this bunch of code:
List<string> NamesList = new List<string>();
List<int> Grades = new List<int>();
Grades.Add(98);
Grades.Add(100);
NamesList.Add("John");
NamesList.Add("Alon");
if (NamesList.Count() == Grades.Count())
{
var length = NamesList.Count();
var min = Grades.Min();
var max = Grades.Max();
using (System.IO.StreamWriter myF =
new System.IO.StreamWriter(#"C:\Users\axcel\textfolder\myFile.txt", true))
{
for (int i = 0; i < length; i++)
{
if (i == 0)
{
myF.WriteLine("Name Age Grade");
myF.WriteLine("==================================");
}
myF.WriteLine(NamesList.ElementAt(i));
myF.WriteLine(" ");
myF.WriteLine(Grades.ElementAt(i));
}
}
}
but my problem is that writing the grades after the names it is writing in a new line. I thought of writing it together to a string and to streaming it but I want to avoid an extra computing...
How can I solve it?
WriteLine() always add a new line after your text. So in your case it should be
myF.Write(NamesList.ElementAt(i));
myF.Write(" ");
myF.WriteLine(Grades.ElementAt(i));
var students = new List<(string name, int age, int grade)>()
{
("John", 21, 98),
("Alon", 45, 100)
};
students.Add(("Alice", 35, 99));
using (var writer = new StreamWriter("myFile.txt"))
{
writer.WriteLine(string.Join("\t", "Name", "Age", "Grade"));
foreach(var student in students)
{
writer.WriteLine(string.Join("\t", student.name, student.age, student.grade));
}
}
As some comments have suggested you could use a Student class to group name, age and grade. In this example I've used a Value Tuple instead.
You can see how it improves the readability of the code and you can focus on the problem you are actually trying to solve. You can reduce your write operation to a simple, readable expression - meaning you are less likely to make mistakes like mixing up Write and WriteLine.
You can always align the text by using string interpolation alignment.
To follow some of the comments, I also urge you to build a class holding the values.
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public int Grade { get; set; }
}
And here is the code using string interpolation alignment
var students = new List<Student>
{
new Student {Name = "John", Age = 10, Grade = 98},
new Student {Name = "Alon", Age = 10, Grade = 100}
};
var minGrade = students.Min(s => s.Grade);
var maxGrade = students.Max(s => s.Grade);
using (var myF = new System.IO.StreamWriter(#"C:\Users\axcel\textfolder\myFile.txt", true))
{
myF.WriteLine($"{"Name",-15}{"Age",-10}{"Grade",5}");
myF.WriteLine("==============================");
foreach (var student in students)
{
myF.WriteLine($"{student.Name,-15}{student.Age,-10}{student.Grade,5}");
}
}
This will produce the following result:
Name Age Grade
==============================
John 10 98
Alon 10 100
Positive numbers are right-aligned and negative numbers are left-aligned
You can read more about it on the string interpolation page at Microsoft Docs
To solve the issue you are having, you could just use:
myF.WriteLine(NamesList.ElementAt(i) + " " + Grades.ElementAt(i));
However the code you provided would benefit from being modified as described in the comments (create a class, use FileHelpers, etc.)
Solution 1:
Why you are not trying the concatenating the two string like:
string line = NamesList.ElementAt(i) + " " + Grades.ElementAt(i);
myF.WriteLine(line);
OR
Solution 2:
What you are using is WriteLine("Text") function which always writes the text to next line. Instead you can use Write("Text") function which will write the string on same line. you can try like:
myF.Write(NamesList.ElementAt(i));
myF.Write(" ");
myF.Write(Grades.ElementAt(i));
myF.WriteLine(); // Here it will enter to new line

C#: read text file and process it

I need a program in C# which is write out
how many Eric Clapton songs played in the radios.
is there any eric clapton song which all 3 radios played.
how much time they broadcasted eric clapton songs in SUM.
The first columns contain the radio identification(1-2-3)
The second column is about the song playtime minutes
the third column is the song playtime in seconds
the last two is the performer : song
So the file looks like this:
1 5 3 Deep Purple:Bad Attitude
2 3 36 Eric Clapton:Terraplane Blues
3 2 46 Eric Clapton:Crazy Country Hop
3 3 25 Omega:Ablakok
2 4 23 Eric Clapton:Catch Me If You Can
1 3 27 Eric Clapton:Willie And The Hand Jive
3 4 33 Omega:A szamuzott
.................
And more 670 lines.
so far i get this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace radiplaytime
{
public struct Adat
{
public int rad;
public int min;
public int sec;
public Adat(string a, string b, string c)
{
rad = Convert.ToInt32(a);
min = Convert.ToInt32(b);
sec = Convert.ToInt32(c);
}
}
class Program
{
static void Main(string[] args)
{
String[] lines = File.ReadAllLines(#"...\zenek.txt");
List<Adat> adatlista = (from adat in lines
//var adatlista = from adat in lines
select new Adat(adat.Split(' ')[0],
adat.Split(' ')[1],
adat.Split(' ')[2])).ToList<Adat>();
var timesum = (from adat in adatlista
group adat by adat.rad into ertekek
select new
{
rad = ertekek.Key,
hour = (ertekek.Sum(adat => adat.min) +
ertekek.Sum(adat => adat.sec) / 60) / 60,
min = (ertekek.Sum(adat => adat.min) +
ertekek.Sum(adat => adat.sec) / 60) % 60,
sec = ertekek.Sum(adat => adat.sec) % 60,
}).ToArray();
for (int i = 0; i < timesum.Length; i++)
{
Console.WriteLine("{0}. radio: {1}:{2}:{3} playtime",
timesum[i].rad,
timesum[i].hour,
timesum[i].min,
timesum[i].sec);
}
Console.ReadKey();
}
}
}
You can define a custom class to store the values of each line. You will need to use Regex to split each line and populate your custom class. Then you can use linq to get the information you need.
public class Plays
{
public int RadioID { get; set; }
public int PlayTimeMinutes { get; set; }
public int PlayTimeSeconds { get; set; }
public string Performer { get; set; }
public string Song { get; set; }
}
So you then read your file and populate the custom Plays:
String[] lines = File.ReadAllLines(#"songs.txt");
List<Plays> plays = new List<Plays>();
foreach (string line in lines)
{
var matches = Regex.Match(line, #"^(\d+)\s(\d+)\s(\d+)\s(.+)\:(.+)$"); //this will split your line into groups
if (matches.Success)
{
Plays play = new Plays();
play.RadioID = int.Parse(matches.Groups[1].Value);
play.PlayTimeMinutes = int.Parse(matches.Groups[2].Value);
play.PlayTimeSeconds = int.Parse(matches.Groups[3].Value);
play.Performer = matches.Groups[4].Value;
play.Song = matches.Groups[5].Value;
plays.Add(play);
}
}
Now that you have your list of songs, you can use linq to get what you need:
//Get Total Eric Clapton songs played - assuming distinct songs
var ericClaptonSongsPlayed = plays.Where(x => x.Performer == "Eric Clapton").GroupBy(y => y.Song).Count();
//get eric clapton songs played on all radio stations
var radioStations = plays.Select(x => x.RadioID).Distinct();
var commonEricClaptonSong = plays.Where(x => x.Performer == "Eric Clapton").GroupBy(y => y.Song).Where(z => z.Count() == radioStations.Count());
etc.
String splitting works only if the text is really simple and doesn't have to deal with fixed length fields. It generates a lot of temporary strings as well, that can cause your program to consume many times the size of the original in RAM and harm performance due to the constant allocations and garbage collection.
Riv's answer shows how to use a Regex to parse this file. It can be improved in several ways though:
var pattern=#"^(\d+)\s(\d+)\s(\d+)\s(.+)\:(.+)$";
var regex=new Regex(pattern);
var plays = from line in File.ReadLines(filePath)
let matches=regex.Match(line)
select new Plays {
RadioID = int.Parse(matches.Groups[1].Value),
PlayTimeMinutes = int.Parse(matches.Groups[2].Value),
PlayTimeSeconds = int.Parse(matches.Groups[3].Value),
Performer = matches.Groups[4].Value,
Song = matches.Groups[5].Value
};
ReadLines returns an IEnumerable<string> instead of returning all of the lines in a buffer. This means that parsing can start immediatelly
By using a single regular expression, we don't have to rebuild the regex for each line.
No list is needed. The query returns an IEnumerable to which other LINQ operations can be applied directly.
For example :
var durations = plays.GroupBy(p=>p.RadioID)
.Select(grp=>new { RadioID=grp.Key,
Hours = grp.Sum(p=>p.PlayTimeMinutes + p.PlayTimeSecons/60)/60,)
Mins = grp.Sum(p=>p.PlayTimeMinutes + p.PlayTimeSecons/60)%60,)
Secss = grp.Sum(p=> p.PlayTimeSecons)%60)
});
A farther improvement could be to give names to the groups:
var pattern=#"^(?<station>\d+)\s(?<min>\d+)\s(?<sec>\d+)\s(?<performer>.+)\:(?<song>.+)$";
...
select new Plays {
RadioID = int.Parse(matches.Groups["station"].Value),
PlayTimeMinutes = int.Parse(matches.Groups["min"].Value),
...
};
You can also get rid of the Plays class and use a single, slightly more complex LINQ query :
var durations = from line in File.ReadLines(filePath)
let matches=regex.Match(line)
let play= new {
RadioID = int.Parse(matches.Groups["station"].Value),
Minutes = int.Parse(matches.Groups["min"].Value),
Seconds = int.Parse(matches.Groups["sec"].Value)
}
group play by play.RadioID into grp
select new { RadioID = grp.Key,
Hours = grp.Sum(p=>p.Minutes + p.Seconds/60)/60,)
Mins = grp.Sum(p=>p.Minutes + p.Seconds/60)%60,)
Secs = grp.Sum(p=> p.Seconds)%60)
};
In this case, no strings are generated for Performer and Song. That's another benefit of regular expressions. Matches and groups are just indexes into the original string. No string is generated until the .Value is read. This would reduce the RAM used in this case by about 75%.
Once you have the results, you can iterate over them :
foreach (var duration in durations)
{
Console.WriteLine("{0}. radio: {1}:{2}:{3} playtime",
duration.RadioID,
duration.Hours,
duration.Mins,
duration.Secs);
}

C# Search Textfile after multiple Datas and fill them into a Datagrid View

I get the datas from an textfile. The File itself is already inserted by ReadAllLines and converted into a string - this works fine for me and I checked the content with a MessageBox.
The Textfile looks like this (This is just 1 line from about thousand):
3016XY1234567891111111ABCDEFGHIJKabcdef+0000001029916XY1111111123456789ABCDEFGHIJKabcdef+00000003801
Now these are 2 records and I need 2 datas from every record.
The "XY Number" - these are the first 16 digits AFTER "16XY" (16XY is always the same value)
Value from the example: XY1234567891111111
The "Price" - that is the 11 digits value after the plus. The last 2 digits specify the amount of Cent.
Value from the example: 102,99$
I Need both of this datas to be in the same row in my Datagrid View and also for all other Datas in this textfile.
All I can imagine is to write a code, which searchs the string after "16XY" and counts the next 16 digits - the same with the Price which searchs for a "plus" and counts the next 11 digits. Just in this case I would need to ignore the first line of the file because there are about 10x"+".
I tried several possibilities to search and count for that values but without any success right now. Im also not sure how to get the datas into the specific Datagrid View.
This is all I have to show at the moment:
List<List<string>> groups = new List<List<string>>();
List<string> current = null;
foreach (var line in File.ReadAllLines(path))
{
if (line.Contains("") && current == null)
current = new List<string>();
else if (line.Contains("") && current != null)
{
groups.Add(current);
current = null;
}
if (current != null)
current.Add(line);
}
//array
string output = string.Join(Environment.NewLine, current.ToArray());
//string
string final = string.Join("", output.ToCharArray());
MessageBox.Show(output);
Thanks in advance!
Create a class or struct to hold data
public class Data
{
String XYValue { set; get; }
Decimal Price { set; get; }
}
Then the reading logic (You might need to add some more checks):
string decimalSeperator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
List<Data> results = new List<Data>();
foreach(string line in File.ReadAllLines(path).Skip(1))
{
if (line == null)
continue;
int indexOfNextXY = 0;
while (true)
{
int indexOfXY = line.IndexOf("16XY", indexOfNextXY) + "16XY".Length;
int indexOfPlus = line.IndexOf("+", indexOfXY + 16) + "+".Length;
indexOfNextXY = line.IndexOf("16XY", indexOfPlus);
string xyValue = line.Substring(indexOfXY - 2, 18); // -2 to get the XY part
string price = indexOfNextXY < 0 ? line.Substring(indexOfPlus) : line.Substring(indexOfPlus, indexOfNextXY - indexOfPlus);
string intPart = price.Substring(0, price.Length - 2);
string decimalPart = price.Substring(price.Length - 2);
price = intPart + decimalSeperator + decimalPart;
results.Add(new Data (){ XYValue = xyValue, Price = Convert.ToDecimal(price) });
if (indexOfNextXY < 0)
break;
}
}
var regex = new Regex(#"\+(\d+)(\d{2})16(XY\d{16})");
var q =
from e in File.ReadLines("123.txt")
let find = regex.Match(e)
where find.Success
select new
{
price = double.Parse(find.Groups[1].Value) + (double.Parse(find.Groups[2].Value) / 100),
value = find.Groups[3]
};
dataGridView1.DataSource = q.ToList();
If you need the whole text file as string, you can manipulate it with .Split method.
The action will look something like this:
var values = final.Split(new string[] { "16XY" }, StringSplitOptions.RemoveEmptyEntries).ToList();
List <YourModel> models = new List<YourModel>();
foreach (var item in values)
{
if (item.IndexOf('+') > 0)
{
var itemSplit = item.Split('+');
if (itemSplit[0].Length > 15 &&
itemSplit[1].Length > 10)
{
models.Add(new YourModel(itemSplit[0].Substring(0, 16), itemSplit[1].Substring(0, 11)));
}
}
}
And you will need some model
public class YourModel
{
public YourModel(string xy, string price)
{
float forTest = 0;
XYNUMBER = xy;
string addForParse = string.Format("{0}.{1}", price.Substring(0, price.Length - 2), price.Substring(price.Length - 2, 2));
if (float.TryParse(addForParse, out forTest))
{
Price = forTest;
}
}
public string XYNUMBER { get; set; }
public float Price { get; set; }
}
After that you can bind it to your gridview.
Given that the "data pairs" are variable each line (and can get truncated to the next line), it is best to use File.ReadAllText() instead. This will give you a single string to work on, eliminating the truncation issue.
var data = File.ReadAllText(path);
Define a model to contain your data:
public class Item {
public string XYNumber { get; set; }
public double Price { get; set; }
}
You can then use regular expressions to find matches and store them in a list:
var list = List<Item>();
var regex = new Regex(#"(XY\d{16})\w+\+(\d{11})");
var match = regex.Match(data);
while (match.Success) {
var ps = match.Group[1].Captures[0].Value.Insert(9, ".");
list.Add(new Item {
XYNumber = match.Group[0].Captures[0].Value,
Price = Convert.ToDouble(ps)
});
match = match.NextMatch();
}
The list can also be used as a data source to a grid view:
gridView.DataSource = list;
Consider employing the Split method. From the example data, I notice there is "16XY" between each value. So something like this:
var data = "3016XY1234567891111111ABCDEFGHIJKabcdef+0000001029916XY1111111123456789ABCDEFGHIJKabcdef+00000003801";
var records = data.Split(new string[] { "16XY" }, StringSplitOptions.RemoveEmptyEntries);
Given the example data this will return the following array:
[0]: "30"
[1]: "1234567891111111ABCDEFGHIJKabcdef+00000010299"
[2]: "1111111123456789ABCDEFGHIJKabcdef+00000003801"
Now it will be easier to count characters in each string and give them meaning in your code.
So we know valuable data is separated by +. Lets split it further and fill a Dictionary<string, double>.
var parsed = new Dictionary<string, double>(records.Length - 1);
foreach (var pairX in records.Skip(1))
{
var fields = pairX.Split('+');
var cents = double.Parse(fields[1]);
parsed.Add(fields[0], cents / 100);
}
// Now you bind to the GridView
gv.DataSource = parsed;
And your 'GridView` declaration should look like this:
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Key" HeaderText="ID" />
<asp:BoundField DataField="Value" HeaderText="Price" />
</Columns>
</asp:GridView>

Having troubles with my math in the program

When I am programming my forum, I am having troubles in differentiating the two digits of information on my csv file.
The issue is: If, there are 2 adults who are wanting to go to a place the price = less
However is it is a single adult the price will equal more.
PROBLEM: The problem that occurs is that ALL my segments think all Adults should get charged less, when some of the transaction charges should show more.
To be precise in information: The File that contains the HolidayTran.CSV has the array[3] that has the information of 1 or 2 adults in the party.
When I carry the function ref double adult is that I am carrying to the top.
Edited - This is the method that my professor at my university wants...yes slow and stupid, but its his practice final, so I am trying to figure out what I am missing.
Yes I understand i am suppose to do my own work, but I hope someone can tell me where my math is wrong in the programming.
Edited # 2 Changed the Variables to help make it clearer. I found the isolated problem, located at the If loop section, of my equation. It has been multiplying everything by two digits instead of 1. How can I create a function where if the message reads either a 1 or a 2, then the proper math would apply?
AdultPricing This function is suppose to choose the proper math, but I tend to lose concept of how to finish off the function proper. If you look at the If loop, you can see where I go wrong...Any Ideas guys?
I am trying to do a if statement bool function, but that is currently not working...I don't know what else is needed atm..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string[] myfile = File.ReadAllLines(#"C:\temp\customerinfo.csv");
var myquery = from mylines in myfile
let myfield = mylines.Split(',')
let names = myfield[1]
let lastname = myfield[2]
let id = myfield[0]
orderby lastname, names
select new { id, names, lastname };
foreach (var listing in myquery) { cmbcustomerinfo.Items.Add(listing.id + " " + listing.names + " " + listing.lastname); }
}
private void cmbcustomerinfo_SelectedIndexChanged(object sender, EventArgs e)
{
//recalling all private void information at the top of the combobox file, inorder to send information to the listbox.
string tempvariable = "";
string iddvariable = "";
string format1 = "{0,55}{1,5}";
string format2 = "{0,-5:d}{1,15:d}{2,20:c}{3,20:c}";
string format3 = "{0,-15}{1,35:c}{2,20:c}";
string format4 = "{0,-15}{1,72:c}";
getCustomerIDFirstName(out tempvariable, out iddvariable);
getcustomerinfo(iddvariable);
//set the required information to connect to the Holiday Transaction. Where we can connect if the ID found in Holiday Matchs the ID in CustomerInfo.CSV,
//then we can show the data of the dates and pricing of the information
string[] transaction = File.ReadAllLines(#"C:\temp\HolidayTrans.csv");
var TransactionQuery = from myLinesshown in transaction
let myfield2 = myLinesshown.Split(',')
let customerid = myfield2[0]
let datestart = myfield2[1]
let numofadults = byte.Parse(myfield2[2])
let numofkids = byte.Parse(myfield2[3])
where customerid == iddvariable
orderby datestart, customerid
select new
{
customerid,
datestart,
numofadults,
numofkids
};
foreach (var staff1 in TransactionQuery)
{
lstInvoice.Items.Clear();
lstInvoice.Items.Add("Purchase Date EndDate Adult Price Kid Price");
//set up all variables used or to be used.
string idgiven = "";
double KidsSubPricing = 0;
double AdultPricing = 0;
double singleddigit = 0;
double TwinAdultPricing = 0;
double totaladult = 0;
double subtotal = 0;
double subtotal1 = 0;
double kidpricing = 3300;
byte NumberOfDaysSpent = 0;
string EndofDays = "";
DateTime daybegin;
DateTime startthedate;
DateTime datebeginning = DateTime.Now;
//set the basic functionality to find the proper ID and date to be shown that corresponses to the person.
foreach (var transactionfound in TransactionQuery)
{
idgiven = transactionfound.customerid;
datebeginning = DateTime.Parse(transactionfound.datestart);
break;
}
//set the datetime interval to show the proper grouping later on
int xyy = datebeginning.Year;
//This is suppose to show where and how I can seperate the transaction of single and double pricing.
foreach (var transactionfound in TransactionQuery)
{
if (transactionfound.numofadults.ToString().Contains("1"))
{
singleddigit = transactionfound.numofadults;
}
if (transactionfound.numofadults.ToString().Contains("2"))
{
TwinAdultPricing = transactionfound.numofadults;
}
if (transactionfound.customerid == idgiven && DateTime.Parse(transactionfound.datestart).Year == xyy)
{
getpackagepriceinfo(transactionfound.datestart,ref singleddigit, ref TwinAdultPricing, ref NumberOfDaysSpent);
KidsSubPricing = transactionfound.numofkids * kidpricing;
//AdultPricing = transactionfound.numofadults * TwinAdultPricing;
subtotal += KidsSubPricing;
subtotal1 += AdultPricing;
daybegin = DateTime.Parse(transactionfound.datestart);
startthedate = daybegin.AddDays(NumberOfDaysSpent);
EndofDays = startthedate.ToString("d");
lstInvoice.Items.Add(string.Format(format2, daybegin, EndofDays, AdultPricing, KidsSubPricing));
}
else
{
lstInvoice.Items.Add(string.Format(format3, "Subtotal Amount",subtotal1, subtotal));
lstInvoice.Items.Add(" ");
getpackagepriceinfo(transactionfound.datestart,ref singleddigit, ref TwinAdultPricing, ref NumberOfDaysSpent);
KidsSubPricing = transactionfound.numofkids * kidpricing;
//AdultPricing = transactionfound.numofadults * singleddigit;
subtotal += KidsSubPricing;
subtotal1 += AdultPricing;
daybegin = DateTime.Parse(transactionfound.datestart);
startthedate = daybegin.AddDays(NumberOfDaysSpent);
EndofDays = startthedate.ToString("d");
lstInvoice.Items.Add(string.Format(format2, daybegin, EndofDays, AdultPricing, KidsSubPricing));
idgiven = transactionfound.customerid;
xyy = DateTime.Parse(transactionfound.datestart).Year;
}
if (idgiven == "")
{
lstInvoice.Items.Clear();
lstInvoice.Items.Add(string.Format(format1, "Sorry no Transaction Found For" + " ", tempvariable));
}
//else
//{
// lstInvoice.Items.Add(string.Format(format3, "Subtotal Amount", subtotal1, subtotal));
// lstInvoice.Items.Add("");
//}
}lstInvoice.Items.Add(string.Format(format3, "Subtotal Amount", subtotal1, subtotal));
}
}
private void getCustomerIDFirstName(out string tempp, out string idd)
{
string[] temp = cmbcustomerinfo.SelectedItem.ToString().Split(' ');
tempp = temp[1];
idd = temp[0];
}
private void getpackagepriceinfo(string date, ref double CostPerSingleAdult, ref double CostPerTwoAdults, ref byte numofdays1)
{
//set the information here, so we can recall the csv file into the main program.
string[] production = File.ReadAllLines(#"C:\temp\PackagePrice.csv");
var productQuery = from myLinesshown in production
let myfield1 = myLinesshown.Split(',')
let numofdays = byte.Parse(myfield1[0])
let startdateshown = myfield1[1]
let twinadult = myfield1[2]
let singlepricing = myfield1[3]
where startdateshown == date
select new
{
numofdays,
startdateshown,
twinadult,
singlepricing
};
//setting the factor of the private function doubles and bytes to be able to get recalled, back to the top.
foreach (var xyz in productQuery)
{
numofdays1 = xyz.numofdays;
CostPerTwoAdults = double.Parse(xyz.twinadult);
CostPerSingleAdult = double.Parse(xyz.singlepricing);
date = xyz.startdateshown;
break;
}
}
//redo the customer information, so we can recall the string of customer id inorder to recall proper functionality.
private void getcustomerinfo(string customerid){
string[] myfile = File.ReadAllLines(#"C:\temp\customerinfo.csv");
var myquery = from mylines in myfile
let myfield = mylines.Split(',')
let names = myfield[1]
let lastname = myfield[2]
let id = myfield[0]
where id == customerid
select new { id, names, lastname};}
}
}
Fix your variable names.
Refactor your code into smaller and tidier methods.
The problem becomes MUCH Clearer
You have a method called getpackagepriceinfo which takes the parameters
(string date, ref double adult, ref double single12, ref byte numofdays1)
where adult is costPerSingleAdult and single12 is costPerTwoAdults
When calling this method you have passed the variable singleddigit to the now renamed paramter costPerTwoAdults. What does that even mean? Did you realize you haven't used this variable within your code
Put code clarity first. When the code is easy to understand and working correctly, then you can start rewriting sections for speed/memory/reduced LoC/experimental language features/whatever other reason, when you do that write a comment showing the codes original intention so that when a bug gets written in to the new code you can find it quickly.
after edit
That's definitely an improvement. You can see now that you've got the number of adults for each transaction, but you've assigned this number into the variable used for pricing. I think you need to refer back to the specification, which I read as "if there are 2 adults and a two adult price, then charge the twin price. Else if there is no two adult price charge 2 * single adult price, else charge the adult price" your code should read the same.
Decimal adultPrice
if ( twoAdults && twinPrice > 0)
adultPrice = twinPrice;
else if ( twoAdults )
adultPrice = 2 * singlePrice;
else
adultPrice = singlePrice;
Note that this won't work if there are more than 2 adults. Not sure if that meets the specification
SO the new Coding is the following. Thanks to James Barrass I was able to properly fix the coding to work. The problem was the following
numberAdults = byte.Parse(Transaction.NumberofAdults);
if(numberAdults == 2)
adultpricing = AdultCost*2;
else
adultpricing = CostSingle;
Because of the following code above the entire coding is able to properly work now.
private void Form1_Load(object sender, EventArgs e)
{
string[] myfile = File.ReadAllLines(#"C:\temp\customerinfo.csv");
var myQuery = from mylines in myfile
let myfield = mylines.Split(',')
let CustomerID = myfield[0]
let CustomerFirstName = myfield[1]
let CustomerLastName = myfield[2]
orderby CustomerID, CustomerLastName, CustomerFirstName
select new {
CustomerID, CustomerFirstName, CustomerLastName
};
foreach (var customerinfo in myQuery) { cmbCustomer.Items.Add(customerinfo.CustomerID + " " + customerinfo.CustomerFirstName + " " + customerinfo.CustomerLastName); }
}
private void getCustomerFirstandID(out string customerfirst, out string idd)
{
string[] tempp = cmbCustomer.SelectedItem.ToString().Split(' ');
customerfirst = tempp[1];
idd = tempp[0];
}
private void getCustomerInfo(string StatedID)
{
string[] myfile = File.ReadAllLines(#"C:\temp\customerinfo.csv");
var myQuery = from mylines in myfile
let myfield = mylines.Split(',')
let CustomerID = myfield[0]
let CustomerFirstName = myfield[1]
let CustomerLastName = myfield[2]
where CustomerID == StatedID
select new
{
CustomerID,
CustomerFirstName,
CustomerLastName
};
}
private void getPackagePriceInfo(DateTime date, ref double CostofAdults, ref double CostofSingle, ref byte NumberofDaysShown)
{
string[] myGivenFile = File.ReadAllLines(#"C:\temp\PackagePrice.csv");
var myPackageTransaction = from myLinesGiven in myGivenFile
let myFieldShown = myLinesGiven.Split(',')
let NumberofDays = myFieldShown[0]
let StartDate = DateTime.Parse(myFieldShown[1])
let TwinAdult = myFieldShown[2]
let SingleAdult = myFieldShown[3]
where StartDate == date
select new {
NumberofDays, StartDate, TwinAdult, SingleAdult
};
foreach (var Package in myPackageTransaction) {
CostofSingle = double.Parse(Package.SingleAdult);
CostofAdults = double.Parse(Package.TwinAdult);
NumberofDaysShown = byte.Parse(Package.NumberofDays);
date = Package.StartDate;
break;
}
}
private void cmbCustomer_SelectedIndexChanged(object sender, EventArgs e)
{
string customerfirstvariable = "";
string iddvariable = "";
getCustomerFirstandID(out customerfirstvariable, out iddvariable);
getCustomerInfo(iddvariable);
string format1 = "{0,55}{1,5}";
string format2 = "{0,-5:d}{1,15:d}{2,20:c}{3,20:c}";
string format3 = "{0,25}{1,19:c}{2,20:c}";
string format4 = "{0,-15}{1,72:c}";
string[] myGivenFile1 = File.ReadAllLines(#"C:\temp\holidaytrans.csv");
var myHolidayTransaction = from myLinesGiven1 in myGivenFile1
let myFieldShown = myLinesGiven1.Split(',')
let CustomerGivenID = myFieldShown[0]
let PackageStartDate = DateTime.Parse(myFieldShown[1])
let NumberofAdults = myFieldShown[2]
let NumberofKids = myFieldShown[3]
where CustomerGivenID == iddvariable
orderby PackageStartDate
select new
{
CustomerGivenID, PackageStartDate, NumberofAdults, NumberofKids
};
lstInvoice.Items.Clear();
// foreach (var transactionfound in myHolidayTransaction) {
//set up all variables used or to be used.
string EndofDays = "";
DateTime daybegin;
DateTime startthedate;
DateTime datebeginning = DateTime.Now;
string idgiven = "";
double AdultCost = 0;
byte DaysUsed = 0;
double adultpricing = 0;
double KidsPricing = 0;
double KidsCost = 3300;
double subtotal = 0;
double totalamt = 0;
double subtotal1 = 0;
double total1 = 0;
double total = 0;
double CostSingle = 0;
byte numberAdults = 0;
double adultgiven = 0;
int xyz = 0;
foreach (var Transaction in myHolidayTransaction)
{
idgiven = Transaction.CustomerGivenID;
lstInvoice.Items.Add("Purchase Date EndDate Adult Price Kid Price");
datebeginning = Transaction.PackageStartDate;
xyz = datebeginning.Year;
break;
}
foreach (var Transaction in myHolidayTransaction)
{
if (Transaction.PackageStartDate.Year == xyz) {
getPackagePriceInfo(Transaction.PackageStartDate,ref AdultCost, ref CostSingle, ref DaysUsed);
KidsPricing = KidsCost * byte.Parse(Transaction.NumberofKids);
numberAdults = byte.Parse(Transaction.NumberofAdults);
if(numberAdults == 2)
adultpricing = AdultCost*2;
else
adultpricing = CostSingle;
subtotal += KidsPricing;
subtotal1 += adultpricing;
total += KidsPricing;
total1 += adultpricing;
daybegin = Transaction.PackageStartDate;
startthedate = daybegin.AddDays(DaysUsed);
EndofDays = startthedate.ToString("d");
lstInvoice.Items.Add(string.Format(format2, startthedate, EndofDays, adultpricing, KidsPricing));}
else
{
lstInvoice.Items.Add(string.Format(format3, "Subtotal Amount", subtotal1, subtotal));
lstInvoice.Items.Add(" ");
getPackagePriceInfo(Transaction.PackageStartDate, ref AdultCost, ref CostSingle, ref DaysUsed);
KidsPricing = KidsCost * byte.Parse(Transaction.NumberofKids);
numberAdults = byte.Parse(Transaction.NumberofAdults);
if (numberAdults == 2)
adultpricing = AdultCost * 2;
else
adultpricing = CostSingle;
subtotal = KidsPricing;
subtotal1 = adultpricing;
total += KidsPricing;
total1 += adultpricing;
daybegin =Transaction.PackageStartDate;
startthedate = daybegin.AddDays(DaysUsed);
EndofDays = startthedate.ToString("d");
lstInvoice.Items.Add(string.Format(format2, startthedate, EndofDays, adultpricing, KidsPricing));
idgiven = Transaction.CustomerGivenID;
xyz = Transaction.PackageStartDate.Year;
}}
if (idgiven == "")
{
lstInvoice.Items.Clear();
lstInvoice.Items.Add(string.Format(format1, "Sorry no Transaction Found For" + " ", customerfirstvariable));
}
else
lstInvoice.Items.Add(" ");
lstInvoice.Items.Add(string.Format(format3, "Subtotal Amount", subtotal1, subtotal));
lstInvoice.Items.Add(" ");
lstInvoice.Items.Add(string.Format(format3, "Total Amount", total1, total));
}
}
}

Create a new row from 2 tables

New to LINQ and c# but having problem with one specific problem.
I have 2 Data tables.
DataTable 1 has Name, House Number in it. DataTable 2 holds Street, FirstHouseNumber, LastHouseNumber.
What I want to do is create a new table that has Name, House Number, Street in it but I keep ending up with lots of DataTable1 Count * DataTable2 Count when I would only expect the same count as DataTable 1.
Heres the expression I am using:
var funcquery = from name in DT1.AsEnumerable()
from street in DT2.AsEnumerable()
where name.Field<int>("Number") >= street.Field<int>("FirstHouseNumber") && name.Field<int>("Number") <= street.Field<int>("LastHouseNumber")
select new { Name = name.Field<string>("Name"), Number = name.Field<int>("Number"), street.Field<string>("Street") };
What am I doing wrong? I just cant get it at all.
TIA.
This will be easier to think about with a more specific example:
Name House Number
------ ------------
Chris 123
Ben 456
Joe 789
Street First House Last House
Number Number
-------- ------------ ------------
Broadway 100 500
Main 501 1000
To demonstrate the results using this data, I use the following code:
class HouseInfo
{
public string Name;
public int HouseNumber;
public HouseInfo(string Name, int HouseNumber)
{
this.Name = Name;
this.HouseNumber = HouseNumber;
}
}
class StreetInfo
{
public string Street;
public int FirstHouseNumber;
public int LastHouseNumber;
public StreetInfo(string Street, int FirstHouseNumber, int LastHouseNumber)
{
this.Street = Street;
this.FirstHouseNumber = FirstHouseNumber;
this.LastHouseNumber = LastHouseNumber;
}
}
static void Main(string[] args)
{
HouseInfo[] houses = new HouseInfo[] {
new HouseInfo("Chris", 123),
new HouseInfo("Ben", 456),
new HouseInfo("Joe", 789) };
StreetInfo[] streets = new StreetInfo[] {
new StreetInfo("Broadway", 100, 500),
new StreetInfo("Main", 501, 1000)};
var funcquery = from name in houses
from street in streets
where name.HouseNumber >= street.FirstHouseNumber && name.HouseNumber <= street.LastHouseNumber
select new { Name = name.Name, Number = name.HouseNumber, Street = street.Street };
var results = funcquery.ToArray();
for (int i = 0; i < results.Length; i++)
{
Console.WriteLine("{0} {1} {2}", results[i].Name, results[i].Number, results[i].Street);
}
}
The results are:
Chris 123 Broadway
Ben 456 Broadway
Joe 789 Main
This seems to be what you are after, so I don't think the problem is in the code you've given. Perhaps it is in the data itself (if multiple streets include the same house range) or something else. You may need to supply more specific data that demonstrates your problem.
Here's the same code implemented using DataTables instead of classes. It yields the same result:
System.Data.DataTable DT1 = new System.Data.DataTable("Houses");
System.Data.DataTable DT2 = new System.Data.DataTable("Streets");
DT1.Columns.Add("Name", typeof(string));
DT1.Columns.Add("Number", typeof(int));
DT2.Columns.Add("Street", typeof(string));
DT2.Columns.Add("FirstHouseNumber", typeof(int));
DT2.Columns.Add("LastHouseNumber", typeof(int));
DT1.Rows.Add("Chris", 123);
DT1.Rows.Add("Ben", 456);
DT1.Rows.Add("Joe", 789);
DT2.Rows.Add("Broadway", 100, 500);
DT2.Rows.Add("Main", 501, 1000);
var funcquery = from System.Data.DataRow name in DT1.Rows
from System.Data.DataRow street in DT2.Rows
where (int)name["Number"] >= (int)street["FirstHouseNumber"]
&& (int)name["Number"] <= (int)street["LastHouseNumber"]
select new { Name = name["Name"], Number = name["Number"], Street = street["Street"] };
var results = funcquery.ToArray();
for (int i = 0; i < results.Length; i++)
{
Console.WriteLine("{0} {1} {2}", results[i].Name, results[i].Number, results[i].Street);
}

Categories