linq how to add symbol to empty lines from csv - c#

I'm using this code to read csv files and make List out of them:
public static IEnumerable<string> appendLineNumbers(IEnumerable<string> lines)
{
return lines
.Select((line, index) => new {
Line = line,
LineNumber = index
.ToString()
.Equals("0", StringComparison.CurrentCultureIgnoreCase)
? "RowNumber"
: (index + 1).ToString()})
.Select(x => $"{x.LineNumber};{x.Line}");
}
As it is right now, when I read a file it will add a RowNumber even to a completed empty row. The result will be :
RowNumer;
But I expect the line to have as many ";" as the Headline. Is it possible to put this into this statement or do I need to make an extra method for this.

I would say right at the beginning remove the empty lines. The rest of your code should work then without this stumblestones of enumerating empty lines:
return lines.Where(x=>!string.IsNullOrWhiteSpace(x))
. // here the rest of your code
If I missunderstood you please drop me a comment

Related

Simple LINQ in string list

A little question for a simple LINQ request. This is my first time with LINQ and still not understand all mechanism.
My structure is something like this
List<string> baseData = new List<string>{"\"10\";\"Texte I need\";\"Texte\"",
"\"50\";\"Texte I need\";\"Texte\"",
"\"1000\";\"Texte I need\";\"Texte\"",
"\"100\";\"Texte I need\";\"Texte\""};
Each line of data is construct with field separator ";" and each field are encapsule with quote ".
I have another List Compose with value i have to find in my first list. And i have the Position in line i have to search. because "Texte I need" can be equal with value i am searching
List<string> valueINeedToFind = new List<string>{"50","100"};
char fieldSeparator = ';';
int fieldPositionInBaseDataForSearch = 0;
int fieldPositionInBaseDataToReturn = 1;
I made a first Linq to extract only Line interested me.
List<string> linesINeedInAllData = baseData.Where(Line => valueINeedToFind.Any(Line.Split(fieldSeparator)[fieldPositionInBaseDataForSearch].Trim('"').Contains)).ToList();
This first request Work Great and now i have only Data Line Interested me.
My problem is I don't want all the line But only a list of the value "Texte I need" in position FieldPositionInBaseDataToReturn.
I have to made another LINQ or can i modify my first to directly get what I need?
Since you will be using the split version of each line more than once, separate out the Split operation and then work on the resulting array:
List<string> linesINeedInAllData = baseData.Select(Line => Line.Split(fieldSeparator))
.Where(splitLine => valueINeedToFind.Any(splitLine[fieldPositionInBaseDataForSearch].Trim('"').Contains))
.Select(splitLine => splitLine[fieldPositionInBaseDataToReturn])
.ToList();
List<string> linesINeedInAllData = baseData.Where(Line => valueINeedToFind.Any(Line.Split(fieldSeparator)[fieldPositionInBaseDataForSearch].Trim('"').Equals)).ToList()
.Select(Line => Line.Split(fieldSeparator)[fieldPositionInBaseDataToReturn].Trim('"').ToList();

How can I read a text file with StreamReader selectively?

I am trying to use a loop to collect all the elements in a text file and select out of those elements specific ones to display.
{
string lines = File.ReadLines(path).Where(line => line.StartsWith("Name: ")).ToString();
foreach (string line in lines)
{
MessageList.Items.Add(lines);
}
}
The idea with this code is for the file stream to parse the entire document and only select the lines that start with Name:, ignoring all other ones so I can add the
I can't seem to get around the syntax error within the condition of the foreach loop. It says I'm trying to convert between char and string and the compiler is confused by my request. I've tried doing this with and without invoking ToString(), I've also tried it by declaring lines as a var instead of a string. I tried to do this without the lambda expression
lines is not a string. It will be of type
IEnumerable<string>
if you let it.
If you use var instead the compiler will figure out the type for you. If you hover your mouse over the lines or line variable in your IDE it will let you know the type.
var lines = File.ReadLines(path).Where(line => line.StartsWith("Name: "));
foreach (var line in lines)
{
MessageList.Items.Add(line);
}
If you want to be explicit about the type, this would be the code.
IEnumerable<string> lines = File.ReadLines(path).Where(line =>
line.StartsWith("Name: "));
foreach (string line in lines)
{
MessageList.Items.Add(line);
}
About var - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/var
About File.Readlines including the return type - https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlines?view=net-6.0
You could also use:-
File.ReadLines(path).Where(line => line.StartsWith("Name: ")).ToList()
.ForEach(line => { MessageList.Items.Add(line); });

Remove lines from List<String[]> using Linq, if meeting a certain criteria

I've searched around for a solution to this question but can't find an applicable circumstance and can't get my head around it either.
I've got a List<String[]> object (a parsed CSV file) and want to remove any rows if the first value in the row is equal to my criteria.
I've tried the following (with variations) and can't seem to get it to delete the lines, it just passes over them:
rows.RemoveAll(s => s[0].ToString() != "Test");
Which I'm currently reading as, remove s if s[0] (the first value in the row) does not equal "Test".
Can someone point me in the right direction for this?
Thanks, Al.
Edit for wider context / better understanding:
The code is as follows:
private void CleanUpCSV(string path)
{
List<string[]> rows = File.ReadAllLines(path).Select(x => x.Split(',')).ToList();
rows.RemoveAll(s => s[0] != "Test");
using (StreamWriter writer = new StreamWriter(path, false))
{
foreach (var row in rows)
{
writer.WriteLine(row);
}
}
}
So the question is -> Why won't this remove the lines that do not start with "Test" and upon writing, why is it returning System.String[] as all the values?
Did you try with Where? Where is going to filter based on a predicate. You should be able to do something like this:
Demo: Try it online!
List<string[]> rows = new List<string[]> { new []{"Test"}, new []{ "Foo"} };
rows = rows.Where(s => s[0] == "Test").ToList();
foreach(var row in rows)
{
Console.WriteLine(string.Join(",", row));
}
output
Test
You dont need ToString() because S[0] is already a string
You may want to handle empty case or s[0] could throw
You can use s.First() instead of s[0]
You can learn more about Predicateon msdn
Edit
For your example:
private void CleanUpCSV(string path)
{
var rows = File.ReadAllLines(path).Select(x => x.Split(','));
using (StreamWriter writer = new StreamWriter(path, false))
{
foreach (var row in rows.Where(s => s[0] == "Test"))
{
writer.WriteLine(string.Join(",", row));
}
}
}
By the way, you may want to use a library to handle csv parsing. I personally use CsvHelper
The only error in your code is the following:
Since row is string[] this
writer.WriteLine(row);
won't give you the result you were expecting.
Change it like this
writer.WriteLine(String.Join(",", row));
To convert the string[]back into its orginal form.
Any other "optimisation" in all the answers proposed here arent really optimal either.
If you're really trying to remove items where the first element isn't "Test", then your code should work, though you don't need to call .ToString() on s[0] since it's already a string. If this doesn't work for you, perhaps your problem lurks elsewhere? If you give an example of your code in a wider context you could get more help
Filter it like this instead:
var filteredList = rows.Where(s => s[0] == "test").ToArray();

Is there a better method of calling a comparision over a list of objects in C#?

I am reading in lines from a large text file. Amongst these file are occasional strings, which are in a preset list of possibilities, and I wish to check the line currently being read for a match to any of the strings in the possibilities list. If there is a match I want to simply append them to a different list, and continue the loop I am using to read the file.
I was just wondering if there is a more efficent way to do a line.Contains() or equivilance check against say the first element in the list, then the second, etc. without using a nested loop or a long if statement filled with "or"s.
Example of what I have now:
List<string> possible = new List<string> {"Cat", "Dog"}
using(StreamReader sr = new StreamReader(someFile))
{
string aLine;
while ((aLine = sr.Readline()) != null)
{
if (...)
{
foreach (string element in possible)
{
if line.Contains(element) == true
{
~add to some other list
continue
}
}
~other stuff
}
}
I don't know about more efficient run-time wise, but you can eliminate a lot of code by using LINQ:
otherList.AddRange(File.ReadAllLines(somefile).
.Where(line => possible.Any(p => line.Contains(p)));
I guess you are looking for:
if(possible.Any(r=> line.Contains(r)))
{
}
You can separate your work to Get Data and then Analyse Data. You don't have to do it in the same loop.
After reading lines, there are many ways to filter them. The most readable and maintenable IMO is to use Linq.
You can change your code to this:
// get lines
var lines = File.ReadLines("someFile");
// what I am looking for
var clues = new List<string> { "Cat", "Dog" };
// filter 1. Are there clues? This is if you only want to know
var haveCluesInLines = lines.Any(l => clues.Any(c => l.Contains(c)));
// filter 2. Get lines with clues
var linesWithClues = lines.Where(l => clues.Any(c => l.Contains(c)));
Edit:
Most likely you will have little clues and many lines. This example checks each line with every clue, saving time.

How to check for list in LINQ

I am trying to read a file and process using LINQ.
I have a exclude list where if i encounter certain words in the file, i should omit that line
my code is
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = from line in File.ReadAllLines(sCodeFile)
let items = line.Split('\n')
where !line.Contains(sIgnoreList.ToString())
select line;
foreach (var item in wordsPerLine)
{
console.WriteLine(item);
}
My LST file looks like below
\voodoo\foo.c
\voodoo\voodoo.h
\voodoo\std.c
\voodoo\foo1.h
in the end i want only
\voodoo\voodoo.h
\voodoo\std.c
How can i process the ignored list in contains? with my above code i dont get the desired output for sure
can any one help?
regards,
Karthik
Revised my answer. The bug is that you're doing a ToString on the ignore list, which certainly will not work. You must check each item in the list, which can be done using something like this:
where !sIgnoreList.Any(ignore => line.Contains(ignore))
A curiosity: since the above lambda is just passing a value into a method that only take the value as a parameter, you can write this even more compact as a method group like this:
where !sIgnoreList.Any(line.Contains)
Try this.
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = File.ReadAllLines(sCodeFile).Where(n =>
{
foreach (var ign in sIgnoreList)
{
if (n.IndexOf(ign) != -1)
return false;
}
return true;
});
It passes the current element (n) to a lambda function, which checks it against every element of the sIgnoreList. Returning false means the element is ignored, true means it's returned.
Change it to:
where !sIgnoreList.Contains(line)
You need to compare each single line and check that it doesn't exist in the ignore list.
That's why the Vladislav's answer did not work.
Here's the working solution:
var result = from line in File.ReadAllLines(codeFile)
where !ignoreList.Any(line.Contains)
select line;
The problem was you didn't want to check for the whole path and messed up words/lines part a bit.

Categories