LINQ unable to OrderBy date in specific column? - c#

I have a CSV file which goes like this:
1,01/01/2001,a
2,19/09/2013,as
3,12/05/2016,asd
4,13/05/2016,asdf
5,12/12/2012,asdfg
6,05/02/2006,asdfgh
7,06/03/2011,asdfghj
I want to sort and display the dates in chronological order but I can't seem to get my code to sort it out. However, my code is able to display the dates according to the format in the CSV file.
If the CSV file is like this:
01/01/2001
19/09/2013
12/05/2016
13/05/2016
12/12/2012
05/02/2006
06/03/2011
Then, it works just fine.
The following is my code:
string parts = new StreamReader(#"C:\input.txt").ReadToEnd();
string[] file = parts.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < file.Length; i++)
{
string[] data = file[i].Split(new string[] { "," }, StringSplitOptions.None);
string[] dates = new string[] { data[1].ToString() };
//var date = dates.OrderBy(x => DateTime.Parse(x)).ToList();
var date = dates.OrderBy(x => DateTime.ParseExact(x, "dd/MM/yyyy", null));
foreach (string s in date)
{
sb.AppendLine(s + "<br />");
Label1.Text = sb.ToString();
}
}
I have tested this code using both DateTime.Parse and DateTime.ParseExact on the CSV file, but they didn't work.
I'm really new to LINQ query and this is my first time using it. I would really appreciate it if there is an explanation provided on what's wrong with my code.

You are sorting an array (dates) that always contains one date (because you create it each time from a single line in a loop). Then you add this line to the label's text.
You have to create a collection that contains all the dates, before you sort it.
string parts = new StringReader(#"C:\input.txt").ReadToEnd();
string[] lines = parts.Split(new string[] {Environment.NewLine}, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
List<string> dates = new List<string>();
for (int i = 0; i < lines.Length; i++)
{
string[] data = lines[i].Split(new string[] { "," }, StringSplitOptions.None);
dates.Add(data[1]);
}
var datesSorted = dates.OrderBy(x => DateTime.ParseExact(x, "dd/MM/yyyy", null));
foreach (string s in datesSorted)
{
sb.AppendLine(s + "<br />");
}
Label1.Text = sb.ToString();

It may required to select the values in Date format first then order it
Try
var date = dates.Select(x => DateTime.ParseExact(x, "dd/MM/yyyy", null)).OrderBy(t => t).ToList();
foreach (DateTime s in date)
{
sb.AppendLine(s.ToString("dd/MM/yyyy") + "<br />");
}
Label1.Text = sb.ToString();
Need to move the Label1 Text assigning after foreach

Please first put your dates into a Collection, for example a List or Array, and then sort the Collection. Default sort order is ascending
List<string> unsortedDates = new List<string>();
string parts = new StreamReader(#"C:\input.txt").ReadToEnd();
string[] file = parts.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < file.Length; i++)
{
string[] data = file[i].Split(new string[] { "," }, StringSplitOptions.None);
unsortedDates.Add(data[1]);
}
var sortedDatesAscending = unsortedDates.OrderBy(x => DateTime.ParseExact(x, "dd/MM/yyyy", null));
foreach (string s in sortedDatesAscending)
{
sb.AppendLine(s + "<br />");
Label1.Text = sb.ToString();
}

Related

How to get data from text resource file and put in combobox

I have many files of data in txt files like this short example
123456
754124
956412
789654
They can have tens of lines in each file. Each file populates a separate combobox. From a static file in a folder I can make it work
string[] fname = {"fridge", "washer", "freezer", "dishwasher"};
for (int i = 0; i < fname.Length; i++)
{
string[] lineOfContents = File.ReadAllLines(#"d:\\temp\\" + fname[i] + ".txt");
ComboBox cmbobox = (ComboBox)this.Controls["cmbobx_" + fname[i]];
foreach (var line in lineOfContents)
{
string[] data = line.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
cmbobox.Items.Add(data[0]);
}
cmbobox.SelectedIndex = 0;
}
but I need to do this when I read the data from embedded resources. I pulled the text files into the project.properties.resources so I have them inside the exe. I understand I will need to stream it out from resources but then I get lost in knowing how to convert the stream with all its newlines etc and format it to add it to the combobox.
I tried many things and the closest I think I have got is as follows although it tells me I have hold of nothing (NULL).
string[] fname = {"fridge", "washer", "freezer", "dishwasher"};
var assembly = Assembly.GetExecutingAssembly();
for (int i = 0; i < fname.Length; i++)
{
string lineOfContents;
string name = fname[i] + ".txt";
using (Stream resourceStream = assembly.GetManifestResourceStream(name))
{
if (resourceStream != null)
{
using (StreamReader reader = new StreamReader(resourceStream))
{
lineOfContents = reader.ReadToEnd();
}
}
}
ComboBox cmbobox = (ComboBox)this.Controls["cmbobx_" + fname[i]];
cmbobox.SelectedIndex = 0;
}
Any help in getting the stream into the combo box would be much appreciated.
Thanks for the link below, I did not get that when I searched so thanks and it helped. The working code before I tidy it up is:
string[] fname = {"fridge", "washer", "freezer", "dishwasher"};
for (int i = 0; i < fname.Length; i++)
{
string resource_data = Properties.Resources.ResourceManager.GetString(fname[i]);
string[] lineOfContents = resource_data.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
ComboBox cmbobox = (ComboBox)this.Controls["cmbobx_" + fname[i]];
foreach (var line in lineOfContents)
{
string[] data = line.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
cmbobox.Items.Add(data[0]);
}
cmbobox.SelectedIndex = 0;
}
Thanks for the links, I did not get that when I searched so thanks and it helped. The working code before I tidy it up is:
string[] fname = {"fridge", "washer", "freezer", "dishwasher"};
for (int i = 0; i < fname.Length; i++)
{
string resource_data = Properties.Resources.ResourceManager.GetString(fname[i]);
string[] lineOfContents = resource_data.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
ComboBox cmbobox = (ComboBox)this.Controls["cmbobx_" + fname[i]];
foreach (var line in lineOfContents)
{
string[] data = line.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
cmbobox.Items.Add(data[0]);
}
cmbobox.SelectedIndex = 0;
}

c# store textbox(file content) lines into array

Hey I am currently stuck. I have the following code that takes a line from a file, based on specific string and returns it, then it can be edited, and applied back to a textbox that contains the file content into the line that the string was initially taken from.
private void citationChange()
{
List<string> matchedList = new List<string>();
string[] linesArr = File.ReadAllLines(fileName);
//find matches
for (int a = 0; a < linesArr.Length; a++)
{
string s = linesArr[a];
if (s.Contains(citation))
{
matchedList.Add(linesArr[a]); //matched
lineBeingEdited = a;
break; //breaks the loop when a match is found
}
}
//output
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
editModuleComboBox.Text = lineData[1];
selectedModuleLabel.Text = lineData[2];
moduleTitleTextBox.Text = lineData[3];
creditsTextBox.Text = lineData[4];
semesterTextBox.Text = lineData[5];
examWeightingTextBox.Text = lineData[6];
examMarkTextBox.Text = lineData[7];
testWeightingTextBox.Text = lineData[8];
testMarkTextBox.Text = lineData[9];
courseworkWeightingTextBox.Text = lineData[10];
courseworkMarkTexbox.Text = lineData[11];
}
}
How can I recreate/change this code but for it to read a textbox instead of a file?
Thanks
Change this:
string[] linesArr = File.ReadAllLines(fileName);
to:
string[] linesArr = theTextBox.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
When reading your file File.ReadAllLines basically splits the entire text by \r\n. So you could do this with your text from the textbox:
exchange this line:
string[] linesArr = File.ReadAllLines(fileName);
to this:
string[] linesArr = YourTextBox.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
or this:
string[] linesArr = YourTextBox.Text.Split(new char[] {'\r', '\n'});

Copying CSV file while reordering/adding empty columns

Copying CSV file while reordering/adding empty columns.
For example if ever line of incoming file has values for 3 out of 10 columns in order different from output like (except first which is header with column names):
col2,col6,col4 // first line - column names
2, 5, 8 // subsequent lines - values for 3 columns
and output expected to have
col0,col1,col2,col3,col4,col5,col6,col7,col8,col9
then output should be "" for col0,col1,col3,col5,col7,col8,col9,and values from col2,col4,col4 in the input file. So for the shown second line (2,5,8) expected output is ",,2,,5,,8,,,,,"
Below code I've tried and it is slower than I want.
I have two lists.
The first list filecolumnnames is created by splitting a delimited string (line) and this list gets recreated for every line in the file.
The second list list has the order in which the first list needs to be rearranged and re concatenated.
This works
string fileName = "F:\\temp.csv";
//file data has first row col3,col2,col1,col0;
//second row: 4,3,2,1
//so on
string fileName_recreated = "F:\\temp_1.csv";
int count = 0;
const Int32 BufferSize = 1028;
using (var fileStream = File.OpenRead(fileName))
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
String line;
List<int> list = new List<int>();
string orderedcolumns = "\"\"";
string tableheader = "col0,col1,col2,col3,col4,col5,col6,col7,col8,col9,col10";
List<string> tablecolumnnames = new List<string>();
List<string> filecolumnnames = new List<string>();
while ((line = streamReader.ReadLine()) != null)
{
count = count + 1;
StringBuilder sb = new StringBuilder("");
tablecolumnnames = tableheader.Split(',').ToList();
if (count == 1)
{
string fileheader = line;
//fileheader=""col2,col1,col0"
filecolumnnames = fileheader.Split(',').ToList();
foreach (string col in tablecolumnnames)
{
int index = filecolumnnames.IndexOf(col);
if (index == -1)
{
sb.Append(",");
// orderedcolumns=orderedcolumns+"+\",\"";
list.Add(-1);
}
else
{
sb.Append(filecolumnnames[index] + ",");
//orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\"";
list.Add(index);
}
// MessageBox.Show(orderedcolumns);
}
}
else
{
filecolumnnames = line.Split(',').ToList();
foreach (int items in list)
{
//MessageBox.Show(items.ToString());
if (items == -1)
{
sb.Append(",");
}
else
{
sb.Append(filecolumnnames[items] + ",");
}
}
//expected format sb.Append(filecolumnnames[3] + "," + filecolumnnames[2] + "," + filecolumnnames[2] + ",");
//sb.Append(orderedcolumns);
var result = String.Join (", ", list.Select(index => filecolumnnames[index]));
}
using (FileStream fs = new FileStream(fileName_recreated, FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(sb.ToString());
}
}
I am trying to make it faster by constructing a string orderedcolumns and remove the second for each loop which happens for every row and replace it with constructed string.
so if you uncomment the orderedcolumns string construction orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\""; and uncomment the append sb.Append(orderedcolumns); I am expecting the value inside the constructed string but when I append the orderedcolumns it is appending the text i.e.
""+","+filecolumnnames[3]+","+filecolumnnames[2]+","+filecolumnnames[1]+","+filecolumnnames[0]+","+","+","+","+","+","+","
i.e. I instead want it to take the value inside the filecolumnnames[3] list and not the filecolumnnames[3] name itself.
Expected value: if that line has 1,2,3,4
I want the output to be 4,3,2,1 as filecolumnnames[3] will have 4, filecolumnnames[2] will have 3..
String.Join is the way to construct comma/space delimited strings from sequence.
var result = String.Join (", ", list.Select(index => filecolumnnames[index]);
Since you are reading only subset of columns and orders in input and output don't match I'd use dictionary to hold each row of input.
var row = tablecolumnnames
.Zip(line.Split(','), (Name,Value)=> new {Name,Value})
.ToDictionary(x => x.Name, x.Value);
For output I'd fill sequence from defaults or input row:
var outputLine = String.Join(",",
filecolumnnames
.Select(name => row.ContainsKey(name) ? row[name] : ""));
Note code is typed in and not compiled.
orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\""; "
should be
orderedcolumns = orderedcolumns+ filecolumnnames[index] + ",";
you should however use join as others have pointed out. Or
orderedcolumns.AppendFormat("{0},", filecolumnnames[index]);
you will have to deal with the extra ',' on the end

How to create a generic text file parser for any find of text file?

Want to create a generic text file parser in c# for any find of text file.Actually i have 4 application all 4 getting input data from txt file format but text files are not homogeneous in nature.i have tried fixedwithdelemition.
private static DataTable FixedWidthDiliminatedTxtRead()
{
string[] fields;
StringBuilder sb = new StringBuilder();
List<StringBuilder> lst = new List<StringBuilder>();
DataTable dtable = new DataTable();
ArrayList aList;
using (TextFieldParser tfp = new TextFieldParser(testOCC))
{
tfp.TextFieldType = FieldType.FixedWidth;
tfp.SetFieldWidths(new int[12] { 2,25,8,12,13,5,6,3,10,11,10,24 });
for (int col = 1; col < 13; ++col)
dtable.Columns.Add("COL" + col);
while (!tfp.EndOfData)
{
fields = tfp.ReadFields();
aList = new ArrayList();
for (int i = 0; i < fields.Length; ++i)
aList.Add(fields[i] as string);
if (dtable.Columns.Count == aList.Count)
dtable.Rows.Add(aList.ToArray());
}
}
return dtable;
}
but i feel its very rigid one and really varies application to application making it configgurable .any better way ..
tfp.SetFieldWidths(new int[12] { 2,25,8,12,13,5,6,3,10,11,10,24 });
File nature :
Its a report kind of file .
position of columns are very similar
row data of file id different .
I get this as a reference
http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F
any other thoughts ?
If the only thing different is the field widths, you could just try sending the field widths in as a parameter:
private static DataTable FixedWidthDiliminatedTxtRead(int[] fieldWidthArray)
{
string[] fields;
StringBuilder sb = new StringBuilder();
List<StringBuilder> lst = new List<StringBuilder>();
DataTable dtable = new DataTable();
ArrayList aList;
using (TextFieldParser tfp = new TextFieldParser(testOCC))
{
tfp.TextFieldType = FieldType.FixedWidth;
tfp.SetFieldWidths(fieldWidthArray);
for (int col = 1; col < 13; ++col)
dtable.Columns.Add("COL" + col);
while (!tfp.EndOfData)
{
fields = tfp.ReadFields();
aList = new ArrayList();
for (int i = 0; i < fields.Length; ++i)
aList.Add(fields[i] as string);
if (dtable.Columns.Count == aList.Count)
dtable.Rows.Add(aList.ToArray());
}
}
return dtable;
}
If you will have more logic to grab the data, you might want to consider defining an interface or abstract class for a GenericTextParser and create concrete implementations for each other file.
Hey I made one of these last week.
I did not write it with the intentions of other people using it so I appologize in advance if its not documented well but I cleaned it up for you. ALSO I grabbed several segments of code from stack overflow so I am not the original author of several pieces of this.
The places you need to edit are the path and pathout and the seperators of text.
char[] delimiters = new char[]
So it searches for part of a word and then grabs the whole word. I used a c# console application for this.
Here you go:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace UniqueListofStringFinder
{
class Program
{
static void Main(string[] args)
{
string path = #"c:\Your Path\in.txt";
string pathOut = #"c:\Your Path\out.txt";
string data = "!";
Console.WriteLine("Current Path In is set to: " + path);
Console.WriteLine("Current Path Out is set to: " + pathOut);
Console.WriteLine(Environment.NewLine + Environment.NewLine + "Input String to Search For:");
Console.Read();
string input = Console.ReadLine();
// Delete the file if it exists.
if (!File.Exists(path))
{
// Create the file.
using (FileStream fs = File.Create(path))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("This is some text in the file.");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
}
System.IO.StreamReader file = new System.IO.StreamReader(path);
List<string> Spec = new List<string>();
using (StreamReader sr = File.OpenText(path))
{
while (!file.EndOfStream)
{
string s = file.ReadLine();
if (s.Contains(input))
{
char[] delimiters = new char[] { '\r', '\n', '\t', ')', '(', ',', '=', '"', '\'', '<', '>', '$', ' ', '#', '[', ']' };
string[] parts = s.Split(delimiters,
StringSplitOptions.RemoveEmptyEntries);
foreach (string word in parts)
{
if (word.Contains(input))
{
if( word.IndexOf(input) == 0)
{
Spec.Add(word);
}
}
}
}
}
Spec.Sort();
// Open the stream and read it back.
//while ((s = sr.ReadLine()) != null)
//{
// Console.WriteLine(s);
//}
}
Console.WriteLine();
StringBuilder builder = new StringBuilder();
foreach (string s in Spec) // Loop through all strings
{
builder.Append(s).Append(Environment.NewLine); // Append string to StringBuilder
}
string result = builder.ToString(); // Get string from StringBuilder
Program a = new Program();
data = a.uniqueness(result);
int i = a.writeFile(data,pathOut);
}
public string uniqueness(string rawData )
{
if (rawData == "")
{
return "Empty Data Set";
}
List<string> dataVar = new List<string>();
List<string> holdData = new List<string>();
bool testBool = false;
using (StringReader reader = new StringReader(rawData))
{
string line;
while ((line = reader.ReadLine()) != null)
{
foreach (string s in holdData)
{
if (line == s)
{
testBool = true;
}
}
if (testBool == false)
{
holdData.Add(line);
}
testBool = false;
// Do something with the line
}
}
int i = 0;
string dataOut = "";
foreach (string s in holdData)
{
dataOut += s + "\r\n";
i++;
}
// Write the string to a file.
return dataOut;
}
public int writeFile(string dataOut, string pathOut)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(pathOut);
file.WriteLine(dataOut);
file.Close();
}
catch (Exception ex)
{
dataOut += ex.ToString();
return 1;
}
return 0;
}
}
}
private static DataTable FixedWidthTxtRead(string filename, int[] fieldWidths)
{
string[] fields;
DataTable dtable = new DataTable();
ArrayList aList;
using (TextFieldParser tfp = new TextFieldParser(filename))
{
tfp.TextFieldType = FieldType.FixedWidth;
tfp.SetFieldWidths(fieldWidths);
for (int col = 1; col <= fieldWidths.length; ++col)
dtable.Columns.Add("COL" + col);
while (!tfp.EndOfData)
{
fields = tfp.ReadFields();
aList = new ArrayList();
for (int i = 0; i < fields.Length; ++i)
aList.Add(fields[i] as string);
if (dtable.Columns.Count == aList.Count) dtable.Rows.Add(aList.ToArray());
}
}
return dtable;
}
Here's what I did:
I built a factory for the type of processor needed (based on file type/format), which abstracted the file reader.
I then built a collection object that contained a set of triggers for each field I was interested in (also contained the property name for which this field is destined). This settings collection is loaded in via an XML configuration file, so all I need to change are the settings, and the base parsing process can react to how the settings are configured. Finally I built a reflection wrapper wherein once a field is parsed, the corresponding property on the model object is set.
As the file flowed through, the triggers for each setting evaluated each lines value. When it found what it was set to find (via pattern matching, or column length values) it fired and event that bubbled up and set a property on the model object. I can show some pseudo code if you're interested. It needs some work for efficiency's sake, but I like the concept.

Why is this code not replacing data in a text file?

I'm working on a small app which should read a file (ANSI 835) and replace data at certain positions with generic data. Basically I'm trying to scrub a person's first and last name from the file.
The line I'm searching for that contains the name looks like this:
NM1*QC*1*Doe*John*R***MI*010088307 01~
My code looks like this:
string[] input_file = (string[])(e.Data.GetData(DataFormats.FileDrop));
string output_file = #"c:\scrubbed.txt";
foreach (string file in input_file)
{
string[] lines = File.ReadAllLines(file);
foreach (string line in lines)
{
if (line.StartsWith("NM1*QC"))
{
line.Split('*')[1] = "Lastname";
line.Split('*')[2] = "Firstname";
}
}
File.WriteAllLines(output_file, lines);
}
The File.WriteAllLines works, but the data isn't being changed. I'm trying to get any line that starts with NM1*QC to look like this:
NM1*QC*1*Lastname*Firstname*R***MI*010088307 01~
There are many lines in the file that start with NM1*QC. What's the proper way to 'find and replace' and then create a new file in this situation?
As always, thanks for your time!
The calls to String.Split return variables that you neither capture, nor use, they do not change the underlying string. So your code equates to this:
if (line.StartsWith("NM1*QC"))
{
string[] split1 = line.Split('*')[1] = "Lastname";
string[] split2 = line.Split('*')[2] = "Firstname";
}
You would need to take the results of split1 and split2 and use those to recreate your string. Here is how I would re-write your code:
string[] input_file = (string[])(e.Data.GetData(DataFormats.FileDrop));
string output_file = #"c:\scrubbed.txt";
foreach (string file in input_file)
{
string[] lines = File.ReadAllLines(file);
for (int i=0; i < lines.length; i++)
{
string line = lines[i];
if (line.StartsWith("NM1*QC"))
{
string[] values = line.Split('*');
values[1] = "Lastname";
values[2] = "Firstname";
lines[i] = String.Join("*", values);
}
}
File.WriteAllLines(output_file, lines);
}
Notice I am recombining the individual values using the String.Join method, and inserting the new string back into the array of lines. That will then get written out as you expect.
Here you are creating a temporary array:
line.Split('*')
And you are changing its contents:
line.Split('*')[1] = "Lastname";
After the line has been executed the reference to this temporary array is lost and along with it go your changes.
In order to persist the changes you need to write directly to lines:
for (var i = 0; i < lines.Length; ++i)
{
var line = lines[i];
if (!line.StartsWith("NM1*QC"))
{
continue;
}
var parts = line.Split('*');
parts[3] = "Lastname";
parts[4] = "Firstname";
lines[i] = string.Join("*", parts);
}

Categories