String list remove - c#

I have this code:
List<string> lineList = new List<string>();
foreach (var line in theFinalList)
{
if (line.PartDescription != "")
lineList.Add(line.PartDescription + " " + line.PartNumber + "\n");
else
lineList.Add("N/A " + line.PartNumber + "\n");
//
//This is what I am trying to fix:
if (lineList.Contains("FID") || lineList.Contains("EXCLUDE"))
// REMOVE THE item in the lineList
}
I am trying to go through theFinalList in a foreach loop and add each line to a new list called lineList.
Once added, I want to remove any entries from that list that contain the text "FID" or "EXCLUDE".
I am having trouble removing the entry, can someone help me?

why add them when you want to remove them right after:
lineList = theFinalList.Select( line =>
{
if (line.PartDescription != "")
return line.PartDescription + " " + line.PartNumber + "\n";
else
return "N/A " + line.PartNumber + "\n";
})
.Where(x => !(x.Contains("FID") || x.Contains("EXCLUDE")))
.ToList();

The following code sample iterates through the lineList and removes lines that contain FID or EXCLUDE.
for(int i = lineList.Count - 1; i >= 0; i--)
{
if (lineList[i].Contains("FID") || lineList[i].Contains("EXCLUDE"))
lineList.RemoveAt(i);
}
It is important to traverse a list in reverse order when deleting items.

You can't remove the items in your theFinalList list while you are iterating over theFinalList in a foreach loop. In this case, you may get System.InvalidOperationException with the message “Collection was modified; enumeration operation may not execute.”
you have to do something like this:
List<string> removals = new List<string>();
foreach (string s in theFinalList)
{
//do stuff with (s);
removals.Add(s);
}
foreach (string s in removals)
{
theFinalList.Remove(s);
}

try
foreach (var line in theFinalList)
{
string T = "";
if (line.PartDescription != "")
T = line.PartDescription + " " + line.PartNumber + "\n";
else
T = "N/A " + line.PartNumber + "\n";
if (!(T.Contains("FID") || T.Contains("EXCLUDE"))
lineList.Add (T);
}

I think its more logical approach
Regex exclude = new Regex("FID|EXCLUDE");
foreach (var line in theFinalList.Where(
ln => !exclude.Match(ln.PartDescription).Success &&
!exclude.Match(ln.PartNumber ).Success))){
string partDescription = "N/A";
if(!string.IsNullOrWhiteSpace(line.PartDescription)){
partDescription = line.PartDescription;
}
lineList.Add(partDescription + " " + line.PartNumber + "\n");
}
edit regex for your needs (ignore case maybe or multiline, probably compiled too) and feel free to replace "\n" with Environment.NewLine

Try this:
var excludingTexts = new [] { "FID", "EXCLUDE" }
lineList = lineList.Where(y => !excludingTexts.Any(x => line.PartDescription.Contains(x) || line.PartNumber.Contains(x))).ToList();
Or you can rewrite it as:
var excludingTexts = new [] { "FID", "EXCLUDE" }
List<string> lineList = (from line in theFinalList
where !excludingTexts.Any(x => line.PartDescription.Contains(x) || line.PartNumber.Contains(x))
select line.PartDescription != "" ?
line.PartDescription + " " + line.PartNumber + "\n" :
"N/A " + line.PartNumber + "\n"
).ToList();

Related

ArgumentOutOfRangeException when I think my code should work

I'm working on a bit of code for school but I keep getting an ArgumentOutOfRangeException
With this code I'm trying to read some data from a .csv file and if it equals the name of the image I want it to remove it from the .csv file whilst keeping the structure intact.
public void checkPair(Image card1, Image card2)
{
this.Image1 = card1;
this.Image2 = card2;
if (Convert.ToString(card1.Source) == Convert.ToString(card2.Source) && (card1 != card2))
{
getPoint(card1, card2);
string path = #"Save1.csv";
var reader = new StreamReader(File.OpenRead(path));
var data = new List<List<string>>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
data.Add(new List<String> { values[0], values[1]
});
}
reader.Close();
string delimiter = ";";
for (int i = 1; i < 5; i++)
{
for (int x = 0; x < 4; x++)
{
if (data[i][x] == Convert.ToString(card1.Source))
{
data[i][x] = null;
}
}
}
File.WriteAllText(path, data[0][0] + delimiter + data[0][1] + Environment.NewLine + data[1][0] + delimiter + data[1][1] + delimiter + data[1][2] + delimiter + data[1][3] + Environment.NewLine + data[2][0] + delimiter + data[2][1] + delimiter + data[2][2] + delimiter + data[2][3] + Environment.NewLine + data[3][0] + delimiter + data[3][1] + delimiter + data[3][2] + delimiter + data[3][3] + Environment.NewLine + data[4][0] + delimiter + data[4][1] + delimiter + data[4][2] + delimiter + data[4][3] + Environment.NewLine + "ready");
I have no idea why I get this error and how to fix it
Initially, I'd change your last line from
File.WriteAllText(path, data[0][0] + delimiter + data[0][1] ....
to something like
var obj1 = data[0][0];
var obj2 = data[0][1];
File.WriteAllText(path, obj1 + delimiter + obj2 .... etc)
If you over inline functions or array accessing, when you get an exception the stack trace won't be that helpful. At least you'll have an idea of the statement that caused the issue.
This technique can prove to be very helpful, if you are looking at an in exception in the logs, after the fact.

how to copy multiple line from text file C#

I need to copy multiple lines from text file(cisco config file): based on the below condition
if the line starts with interface copy from interface until '! '
my file is like :
!
access-list 1>
!
interface 1
ip address xx.xx.xx.xx
!
interface 2
ip address xx.xx.xx.xx
!
route 1
!
I try the below code :
var lines = File.ReadAllLines("C:\\My File2.txt");
foreach (var line1 in lines){
string firstWord = line1.Split(' ').First();
if ((firstWord == "access-list") && (!line1.Contains("remark ")))
{
TextBox1.Text = TextBox1.Text + "\r\n" + line1;
}
else if (firstWord == "nat")
{
TextBox2.Text = TextBox2.Text + "\r\n" + line1;
}
else if (firstWord == "interface")
{
var result = lines.Substring(line1.LastIndexOf('!') + 1);
TextBox3.Text = TextBox3.Text + "\r\n" + result;
}
but I get only one line as output
In case you want to keep your algorithm, this will work for you.
var lines = File.ReadAllLines("C:\\My File2.txt");
int i;
for (i = 0; i<lines.Length;i++)
{
var line1 = lines[i];
if (line1 == "!" || line1 == " ") continue;
if (line1.StartsWith("access-list")) && (!line1.Contains("remark ")))
{
TextBox1.Text = TextBox1.Text + "\r\n" + line1;
}
else if (line1.StartsWith("nat"))
{
TextBox2.Text = TextBox2.Text + "\r\n" + line1;
}
if (line1.StartsWith("interface"))
{
var str = line1;
while (!Equals(lines[i + 1], "!"))
{
str += lines[i + 1];
i++;
}
TextBox3.Text = TextBox3.Text + "\r\n" + str;
}
}
As per the file structure shown by you interface and ip address are on different lines. So you won't get it in same iteration of for loop. When you find that firstWord == "interface" you will need to set a flag that will tell you that next line is ip address and in next iteration check if that flag is true parse the current line as ip address and process it the way you want.
You should use "File.ReadAllText" instead of "File.ReadAllLines". "File.ReadAllText" returns a string with the complete text file text. After that, you can use the "String.Split" method to generate a string array.
var lines = File.ReadAllText("C:\\My File2.txt");
var seperatedStrings = lines.Split('!');
Each index of "seperatedStrings" contains what you want.
UPDATE: Here is a code snippet, that can help:
var lines = File.ReadAllText("C:\\My File2.txt");
var seperatedStrings = lines.Split('!');
foreach (var oneString in seperatedStrings)
{
if (oneString.Contains("access-list"))
{
Console.WriteLine("Access-List: " + oneString);
}else if (oneString.Contains("nat"))
{
Console.WriteLine("Nat: " + oneString);
}else if (oneString.Contains("interface"))
{
Console.WriteLine("Interface: " + oneString);
}
}
This is the output of my code snippet:

Pass multiple data from one function to label in C#

I have a function that retrieves multiple lines of data and I want to display them in a label. My function is as shown below.
public static string GetItemByQuery(IAmazonSimpleDB simpleDBClient, string domainName)
{
SelectResponse response = simpleDBClient.Select(new SelectRequest()
{
SelectExpression = "Select * from " + domainName
});
String res = domainName + " has: ";
foreach (Item item in response.Items)
{
res = item.Name + ": ";
foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attributes)
{
res += "{" + attribute.Name + ", " + attribute.Value + "}, ";
}
res = res.Remove(res.Length - 2);
}
return res;
}
So far I can only return a string which is the last line of the retrieved data. How can I retrieve all the records? I tries arraylist, but it seems that the AWS web application doesn't allow me to use arraylist. Can anyone please help me to solve this??
Return it as as a Enumberable,
List<String> Results ;
Your method would be
public static List<String> GetItemByQuery(IAmazonSimpleDB simpleDBClient, string domainName)
{
List<String> Results = null;
SelectResponse response = simpleDBClient.Select(new SelectRequest()
{
SelectExpression = "Select * from " + domainName
});
String res = domainName + " has: ";
foreach (Item item in response.Items)
{
Results = new List<String>();
res = item.Name + ": ";
foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attributes)
{
res += "{" + attribute.Name + ", " + attribute.Value + "}, ";
}
res = res.Remove(res.Length - 2);
Results.Add(res);
}
return Results;
}

Taglib array exception when setting Artist field

I keep getting an array out of bounds exception with Taglib.tag.Performers in this function that edits ID3 data. I read elsewhere that clearing tag.performers[] can help (if null) but I still get the error sometimes.
Error message:
"Index was outside the bounds of the array.Data:
'System.Collections.ListDictionaryInternal' for test.mp3"
var fileArr = Directory.GetFiles(BasePath, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".mp3") || s.EndsWith(".m4a")).ToArray();
foreach (var file in fileArr)
{
string fileName = Path.GetFileName(file);
string tagArtist = "";
string tagTitle = "";
string tempRegFilename = fileName;
string title = "";
//Apply to tag
TagLib.File mp3tag = TagLib.File.Create(file);
if (mp3tag.Tag.Title != null && mp3tag.Tag.Title.Length > 1)
{
title = mp3tag.Tag.Title;
}
else
{
mp3tag.Tag.Title = String.Empty;
}
if (mp3tag.Tag.Performers[0].Length < 1 || mp3tag.Tag.Performers[0] == null)
{
mp3tag.Tag.Performers[0] = null;
mp3tag.Tag.Performers = new[] { String.Empty };
mp3tag.Save();
}
if (mp3tag.Tag.Performers[0].Length > 1)
{
string[] performers = mp3tag.Tag.Performers;
if (title.Length > 2 && performers[0].Length > 1)
{
tagTitle = title;
tagArtist = performers[0].ToString();
Log.Info("ID3 Artist: " + "[" + tagArtist + "]");
Log.Info("ID3 Title: " + "[" + tagTitle + "]");
Log.Info("Tag data OK");
}
}
//Get artist from filename
if (mp3tag.Tag.Performers[0].Length < 1 || mp3tag.Tag.Performers == null)
{
mp3tag.Tag.Performers = new[] { String.Empty };
string prevArtist = String.Empty;
if (tempRegFilename.Contains("-"))
{
Log.Info("Artist data missing...");
string[] words = tempRegFilename.Split('-');
{
words[0] = words[0].Trim();
string perf = words[0];
mp3tag.Tag.Performers = new[] { perf };
Log.Info("Artists changed from \'" + prevArtist + "\' to " + "'" + perf + "'" + "\r\n");
mp3tag.Save();
}
}
mp3tag.Save();
}
}
catch (Exception ex)
{
Log.Error("TAG EXCEPTION: " + ex.Message + "Data: " + "'" + ex.Data + "'" + " for " + fileName + "\r\n" + ex.HelpLink);
}
Can anyone see what's wrong? I don't have much experience and could use the help. Thanks.
You seem to be assuming in a number of places that mp3Tag.Tag.Performers will have at least one element in it. If it doesn't, then you'll get the exception that you mention whenever you try to access mp3tag.Tag.Performers[0]
It looks like you may be trying to catch that possibility with this code:
if (mp3tag.Tag.Performers[0].Length < 1 || mp3tag.Tag.Performers[0] == null)
{
mp3tag.Tag.Performers[0] = null;
mp3tag.Tag.Performers = new[] { String.Empty };
mp3tag.Save();
}
But your logic is incorrect: you're getting the first element from the array (apparently a string) and checking its length, instead of checking the length of the array itself. Try this:
if (mp3tag.Tag.Performers.Length < 1 || mp3tag.Tag.Performers[0] == null)
{
mp3tag.Tag.Performers = new[] { String.Empty };
mp3tag.Save();
}
PS: It'll be much easier for you to see where your errors are if your log includes the stack trace of the exception, rather than just its message. I typically find it best to just use the ToString() on the exception itself.

Make string checker more efficient

I am using the following code to check if a string is contained within another string -
foreach (string testrecord in testlist)
{
foreach (string realrecord in reallist)
{
if ((Regex.Replace(testrecord , "[^0-9a-zA-Z]+", "")
.Contains((
Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "")))
&&
((Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "") != "")
&&
((Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "").Length >= 4)))))
{
matchTextBox.AppendText("Match: " + testrecord + " & " + realrecord + Environment.NewLine);
}
}
}
However the runtime for this to finish is taking quite a while. Since I added the special character regex removal the runtime is taking a lot longer however the regex is definitely required.
Is there a more efficient way of applying this regex? I tried to add it to the foreach string variables however you cannot alter them as they are in a foreach loop.
Optimized version:
// Do not put text into matchTextBox direct:
// it makes the control re-painting each time you change the text
// Instead, collect all the text into StringBuffer
StringBuilder Sb = new StringBuilder();
// Pull out as much as you can from the inner loop,
// that's why I've changed the loops' order:
// first loop on reallist, then on testlist
foreach (string realrecord in reallist) {
// Cache Regex.Replace result
String realCleaned = Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "");
// Test as early as possible
if (realCleaned.Length < 4)
continue;
// You don't need to test realCleaned != "";: realCleaned.Length < 4 is enough
foreach (string testrecord in testlist) {
// Cache Regex.Replace result: it's a little bit overshoot here, but if some
// more tests are added it'll be helpful
String testCleaned = Regex.Replace(testrecord, "[^0-9a-zA-Z]+", "");
if (testCleaned.Contains(realCleaned))
Sb.AppendLine("Match: " + testrecord + " & " + realrecord);
}
}
// At last matchTextBox.Text change
matchTextBox.AppendText(Sb.ToString());
This should be a bit quicker (one regex operation per testrecord):
var strippedRealList = reallist.Select(s => Regex.Replace(s, "[^0-9a-zA-Z]+", ""))
.Where(s => s.Length >= 4)
.ToArray();
foreach (string realrecord in reallist)
{
strippedRealList.Where(s => realrecord.Contains(s))
.ToList()
.ForEach(s =>
matchTextBox.AppendText("Match: "
+ s
+ " & "
+ realrecord
+ Environment.NewLine));
}
I wonder that you are using Regex to achieve your purpose ignoring the fact that you can also achieve this by only using .Contains() method such that your code should be simple and faster then before
foreach (string testrecord in testlist)
{
foreach (string realrecord in reallist)
{
if(testrecord.Contains(realrecord))
{
matchTextBox.AppendText("Match: " + testrecord + " & " + realrecord + Environment.NewLine);
}
}
}

Categories

Resources