Attempting to output an IOrderedEnumerable list using C# - c#

I have read in a .csv file, done some formatting, seperated each line into its columns and added the resulting arrays into a list of arrays of columns. Next I have ordered the list of arrays using IOrderedEnumerable to order it by the second column ascending alphabetically, then I attempt to out put this newly ordered list to the screen. Its the last part that am I stuck on.
This is what I have attempted:
// attempt to read file, if it fails for some reason display the exception error message
try
{
// create list for storing arrays
List<string[]> users = new List<string[]>();
string[] lineData;
string line;
// read in stremreader
System.IO.StreamReader file = new System.IO.StreamReader("dcpmc_whitelist.csv");
// loop through each line and remove any speech marks
while((line = file.ReadLine()) != null)
{
// remove speech marks from each line
line = line.Replace("\"", "");
// split line into each column
lineData = line.Split(';');
// add each element of split array to the list of arrays
users.Add(lineData);
}
//sort this list by username ascending
IOrderedEnumerable<String[]> usersByUsername = users.OrderBy(user => user[0]);
// display the newly ordered list
for (int i = 0; i <= users.Count; i++)
{
Console.WriteLine(usersByUsername[i]);
}
// after loading the list take user to top of the screen
Console.SetWindowPosition(0, 0);
}
catch (Exception e)
{
// Let the user know what went wrong when reading the file
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
But this give the error:
cannot apply indexing with [] to an expression of type
system.linq.iorderedenumerable
What is causing this error and how can I simply output the newly ordered list correctly?

The cause is neither IEnumerable nor IOrderedEnumerable support indexing, showing you the error.
To display the ordered result you can use foreach to enumerate the collection:
// display the newly ordered list
foreach (var user in usersByUsername)
{
Console.WriteLine(string.Join(", ", user));
}
Or you can convert result to list and use indexing:
//sort this list by username ascending
IList<String[]> usersByUsername = users.OrderBy(user => user[0]).ToList();
// display the newly ordered list
for (int i = 0; i <= users.Count; i++)
{
Console.WriteLine(string.Join(", ", usersByUsername[i]));
}
Also note the usage of string.Join - just printing string[] might not give you the result you expect.

Related

How can I add an List with different data after loop beside existing list?

so bassicly I've got 2 different List
List<string> result = new List<string>;
List<string> outputs= new List<string>;
the outputs List getting filled in an loop and after the loop ends it will add the outputs list in the result list.
So that outputs will have this different values after the loop ends.
For example after the first loop ends the output list has data like:
exampleData1
exampleData2
exampleData3
and so on
so that the result List would now have this three example data.
Now the loop will hit again and will load different data like:
exampleData1.1
exampleData2.1
exampleData3.1
and now It should add this three example data to the result list:
so that now the result list should look like that:
exampleData1, exampleData1.1
exampleData2, exampleData2.1
exampleData3, exampleData3.1
and so should It go on after each loop after the output list changed it should be add side by side like one string.
I have tried things like that:
foreach (var (item1, item2) in output.Zip(output, (xo, y) => (xo, y)))
result.Add($" {item1}, {item2}");
but that's only adding the existing list side by side so that I have 2x the same value side by side.
I hope I have explained it understandable and if not, pls let me know.
Zip is good idea, but care. From the documentation :
If the sequences do not have the same number of elements, the method merges sequences until it reaches the end of one of them.
As result is empty, the end is reached before to start. The solution is to complete with the elements not iterated by Zip :
result = result.Zip(outputs, (r, o) => r + ", " + o)
.Union(result.Skip(outputs.Count)) // Add element from result not iterated by Zip
.Union(outputs.Skip(result.Count)) // Add element from outputs not iterated by Zip
.ToList();
Have look at this: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.zip?view=net-7.0 I think you have a mistake in your code. You're "zipping" outputs with outputs (i.e. itself) you need to zip it with result.
var mergedLists = result.Zip(outputs, (first, second) => first + " " + second);
UPDATED
Here's a full console app solution:
List<string> result = new();
for (var i = 0; i < 3; i++)
{
List<string> outputs = new();
for (var j = 0; j < 3; j++)
{
outputs.Add($"TestData{i}.{j}");
}
if (!result.Any())
{
result = outputs.ToList();
}
else
{
result = result.Zip(outputs, (first, second) => first + " " + second).ToList();
}
}
Console.WriteLine(string.Join(Environment.NewLine, result));
Console.ReadLine();

Parse CSV File section wise

I am new to in C# need help to write parser for below cvs file of data
[INFO]
LINE_NAME,MACHINE_SN,MACHINE_NAME,OPERATOR_ID
LineName,ParmiMachineSN,PARMI_AOI_1,engineer
[INFO_END]
[PANEL_INSP_RESULT]
MODEL_NAME,MODEL_CODE,PANEL_SIDE,INDEX,BARCODE,DATE,START_TIME,END_TIME,DEFECT_NAME,DEFECT_CODE,RESULT
E11-03356-0388-A-TOP CNG,,BOTTOM,47,MLT0388A03358CSNSOF1232210200052-0001,20201023,12:46:57,12:47:04,,,OK
[PANEL_INSP_RESULT_END]
[BOARD_INSP_RESULT]
BOARD_NO,BARCODE,DEFECT_NAME,DEFECT_CODE,BADMARK,RESULT
1,MLT0388A03358CSNSOF1232210200052-0001,,,NO,OK
2,MLT0388A03358CSNSOF1232210200052-0004,,,NO,OK
3,MLT0388A03358CSNSOF1232210200052-0003,,,NO,OK
4,MLT0388A03358CSNSOF1232210200052-0002,,,NO,OK
[BOARD_INSP_RESULT_END]
[COMPONENT_INSP_RESULT]
BOARD_NO,LOCATION_NAME,PIN_NUMBER,POS_X,POS_Y,DEFECT_NAME,DEFECT_CODE,RESULT
[COMPONENT_INSP_RESULT_END]
I need to parse the above file
To parse the above CSV file in C#, you can use the following steps:
Read the entire file into a string using the File.ReadAllText method.
string fileText = File.ReadAllText("file.csv");
Split the file into individual sections by looking for the "[INFO]" and "
[INFO_END]" tags, and then use a loop to process each section.
string[] sections = fileText.Split(new string[] { "[INFO]", "[INFO_END]", "[PANEL_INSP_RESULT]", "[PANEL_INSP_RESULT_END]", "[BOARD_INSP_RESULT]", "[BOARD_INSP_RESULT_END]", "[COMPONENT_INSP_RESULT]", "[COMPONENT_INSP_RESULT_END]" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string section in sections)
{
//Process each section
}
Within the loop, use the String.Split method to split each section into rows by looking for the newline character.
string[] rows = section.Split('\n');
Use the String.Split method again to split each row into cells by looking for the comma.
foreach (string row in rows)
{
string[] cells = row.Split(',');
//Process each cell
}
Now you can process each cell as you need, you can check the first cell value to decide which section this row belongs to, and then you can process the cells according to their type and position in the row.
You can use a switch case statement to check which section you are currently processing and then use appropriate logic to parse the data.
Please be aware that this is a simplified example, and you may need to add additional error handling and validation to ensure that the data is properly parsed.
This is an example how you can parse the csv file but you might need to handle various edge cases like empty rows, empty cells, etc based on your specific use case.
The following reads all text, creates an anonymous list with line index and line followed by looping through a list of sections. In the loop find a section and in this case displays to a console window.
internal partial class Program
{
static void Main(string[] args)
{
var items = (File.ReadAllLines("YourFileNameGoesHere")
.Select((line, index) => new { Line = line, Index = index })
.Select(lineData => lineData)).ToList();
List<string> sections = new List<string>()
{
"INFO",
"PANEL_INSP_RESULT",
"BOARD_INSP_RESULT",
"COMPONENT_INSP_RESULT"
};
foreach (var section in sections)
{
Console.WriteLine($"{section}");
var startItem = items.FirstOrDefault(x => x.Line == $"[{section}]");
var endItem = items.FirstOrDefault(x => x.Line == $"[{section}_END]");
if (startItem is not null && endItem is not null)
{
bool header = false;
for (int index = startItem.Index + 1; index < endItem.Index; index++)
{
if (header == false)
{
Console.WriteLine($"\t{items[index].Line}");
header = true;
}
else
{
Console.WriteLine($"\t\t{items[index].Line}");
}
}
}
else
{
Console.WriteLine("\tFailed to read this section");
}
}
}
}

Is It possible to find out what are the common part in String List

I was working on finding out the Common string part in the String list. If we take a sample data set
private readonly List<string> Xpath = new List<string>()
{
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(1)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(2)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(3)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(4)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(5)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(6)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(7)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(8)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(9)>H2:nth-of-type(1)"
};
From this, I want to find out to which children these are similar. data is an Xpath list.
Programmatically I should be able to tell
Expected output:
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV
In order to get this What I did was like this. I separate each item by > and then create a list of items for each dataset originally.
Then using this find out what are the unique items
private IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
{
hs.IntersectWith(lists[i]);
}
return hs;
}
Able to find out the unique values and create a dataset again. But what happened is if this contains Ex:- Div in two places and it also in every originally dataset even then this method will pick up only one Div.
From then I would get something like this:
BODY>MAIN:nth-of-type(1)>DIV>SECTION
But I need this
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-
type(3)>DIV>ARTICLE>DIV>DIV>DIV
Disclaimer: This is not the most performant solution but it works :)
Let's start with splitting the first path by > character
Do the same with all the paths
char separator = '>';
IEnumerable<string> firstPathChunks = Xpath[0].Split(separator);
var chunks = Xpath.Select(path => path.Split(separator).ToList()).ToArray();
Iterate through the firstPathChunks
Iterate through the chunks
if there is a match then remove the first element
if all first element is removed then append the matching prefix to sb
void Process(StringBuilder sb)
{
foreach (var pathChunk in firstPathChunks)
{
foreach (var chunk in chunks)
{
if (chunk[0] != pathChunk)
{
return;
}
chunk.RemoveAt(0);
}
sb.Append(pathChunk);
sb.Append(separator);
}
}
Sample usage
var sb = new StringBuilder();
Process(sb);
Console.WriteLine(sb.ToString());
Output
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>
Parsing the string by the seperator > is a good idea. Instead of then creating a list of unique items you should create a list of all items contained in the string which would result in
{
"BODY",
"MAIN:nth-of-type(1)",
"DIV",
"SECTTION",
"DIV",
...
}
for the first entry of your XPath list.
This way you create a List<List<string>> containing every element of each entry of your XPath list. You then can compare all first elements of the inner lists. If they are equal save that elements value to you output and proceed with all second elements and so on until you find an element that is not equal in all outer lists.
Edit:
After seperating your list by the > seperator this could look something like this:
List<List<string>> XPathElementsLists;
List<string> resultElements = new List<string>();
string result;
XPathElementsLists = ParseElementsFormXPath(XPath);
for (int i = 0; i < XPathElementsLists[0].Count; i++)
{
bool isEqual = true;
string compareElemment = XPathElementsLists[0][i];
foreach (List<string> element in XPathElementsLists)
{
if (!String.Equals(compareElemment, element))
{
isEqual = false;
break;
}
}
if (!isEqual)
{
break;
}
resultElements.Add(compareElemment);
}
result = String.Join(">", resultElements.ToArray());

How to split a csv file into multiple Lists

I have this code:
//new List
List<string> lines = new List<string>();
List<string> lines2 = new List<string>();
// read and write data to list
for (int i = 0; i < fileName.Length; i++)
{
string file = #"read\" + Path.GetFileName(fileName[i]);
// load rows to list.
lines = File.ReadLines(file).ToList();
foreach (string line in lines)
{
// Variablen für lines
string[] entries = line.Split(';');
int length = entries.Length;
}
}
I am able to read all lines from my csv file into one list but I would like to split the csv file after the 6th column into a second list. How do I do that?
I tired already linq with lines.Take(6).ToList(); but this just reads the first 6 lines if I'm not mistaken. Same for Skip().
You are on the right track, to use Take and Skip, and then to add the items to their respective lists, you can call the AddRange method to add a group of items at once:
var filePath = #"c:\public\temp\temp.txt";
var firstSixColumns = new List<string>();
var restOfColumns = new List<string>();
foreach(var fileLine in File.ReadLines(filePath))
{
var fileLineParts = fileLine.Split(';');
firstSixColumns.AddRange(fileLineParts.Take(6)); // Add the first 6 entries
restOfColumns.AddRange(fileLineParts.Skip(6)); // Add the rest of the entries
}
If, on the other hand, you are trying to just split the csv lines into two groups of columns, without splitting them further (so each line in firstSixLines would represent a row of six columns), then you can use string.Join to stitch the columns back again before adding them:
foreach(var fileLine in File.ReadLines(filePath))
{
var fileLineParts = fileLine.Split(';');
// Join the items before adding them to their respective lists
firstSixColumns.Add(string.Join(";", fileLineParts.Take(6)));
restOfColumns.Add(string.Join(";", fileLineParts.Skip(6)));
}
You need to apply skip at the point you are reading the column values
List<string> entries = line.Split(';').Skip(6).ToList();
That said there are plenty of libraries that you can use that are built around reading CSV files. I would recommend searching NuGet for one before you reinvent the wheel.
If you want 2 lists
string[] entries = line.Split(';');
List<string> entriesFirst = entries.Take(6).ToList();
List<string> entriesSecond = entries.Skip(6).ToList();

c# List Query without using Linq

I'm after some help with how to query a list and return back the index, but not using Linq. I've seen many example where Linq is used, but the software I'm writing the query into doesn't support Linq. :(
So example to get us going:
List<string> location = new List<string>();
location.add(#"C:\test\numbers\FileName_IgnoreThis_1.jpg");
location.add(#"C:\test\numbers\FileName_IgnoreThis_2.jpg");
location.add(#"C:\test\numbers\FileName_ImAfterThis_3.jpg");
location.add(#"C:\test\numbers\FileName_IgnoreThis_4.jpg");
location.add(#"C:\test\numbers\FileName_ImAfterThis_5.jpg");
So this list will be populated with probably a few hundred records, what I need to do is query the list for the text "ImAfterThis" then return the index number location for this item into a string array but without using Linq.
The desired result would be 2 and 4 being added to the string array.
I was thinking of doing a for loop through the list, but is there a better way to achieve this?
List<int> results = new List<int>();
int i = 0;
foreach (string value in location)
{
if (value.Contains("IAfterThis"))
{
results.Add(i);
Console.WriteLine("Found in Index: " + i);
}
i++;
}
Console.ReadLine();
Thanks in advance.
If you want to get just the first occurrence you could simply use the IndexOf() method.
If you want all occurrences of string "whatever" then a for loop would certainly work for you. For the sake of argument here I've capture the indexes in another list:
string MyString = "whatever";
List<int> indexes = new List();
for (int i = 0; i < location.Count; i++)
{
if (location[i] == MyString)
{
indexes.Add(i);
}
}

Categories

Resources