I have a method in a C# program. It enumerates all the .cs files in a certain folder and then runs through the list. For each file, I read all the lines using File.ReadAllLines on it. I only want to process a file if it contains a class, whether conventional, static, or abstract, whose name begins with a certain phrase and does not end with the word Tests. Moreover, I wish to find the line index in the line of lines containing the declaration of the class --- i.e., the public static class Foo part.
Given that I take the result of File.ReadAllLines and call ToList() on it to create a List<string>, I wish to use the FindIndex method to find the index of the line matching my criteria (if it exists) using a Predicate.
My question is: What is a good way to write such a predicate?
I realize I could probably use more sophisticated methods, but I am just putting this code into a quick-and-dirty LINQPad script. So, I don't have to get super fancy.
Let me show you what I have so far (assume that the outermost namespace and class are already suitably declared):
void Main()
{
var files = Directory
.EnumerateDirectories(
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile
), #"source\repos\astrohart\MFR"
), "*", SearchOption.TopDirectoryOnly
).SelectMany(
x => Directory.EnumerateFiles(
x, "FileSystemEntry*.cs", SearchOption.AllDirectories
)
)
.Where(x => !"FileSystemEntry.cs".Equals(Path.GetFileName(x)))
.ToList();
if (files == null || !files.Any()) return;
foreach (var file in files)
{
var contents = string.Empty;
try
{
contents = File.ReadAllText(file);
}
catch (Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
contents = string.Empty;
}
if (string.IsNullOrWhiteSpace(contents)) continue;
if (contents.Contains("[TestFixture]")) continue;
if (contents.Contains("[Log(AttributeExclude = true)]")) continue;
file.Dump();
var lines = new List<string>();
lines.TrimExcess();
try
{
lines = File.ReadAllLines(file).ToList();
}
catch (Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
lines = new List<string>();
lines.TrimExcess();
}
if (lines == null || !lines.Any()) continue;
var index = -1;
for (var i = 0; i < lines.Count; i++)
{
var currentLine = lines[i].Trim();
if (currentLine.EndsWith("Tests")) continue;
if (currentLine.StartsWith("public static class FileSystemEntry"))
{
index = i;
break;
}
if (currentLine.StartsWith("public class FileSystemEntry"))
{
index = i;
break;
}
if (currentLine.StartsWith("public abstract class FileSystemEntry"))
{
index = i;
break;
}
}
if (index < 0) continue;
/*...*/
}
}
How do I translate the for loop in:
var index = -1;
for (var i = 0; i < lines.Count; i++)
{
var currentLine = lines[i].Trim();
if (currentLine.EndsWith("Tests")) continue;
if (currentLine.StartsWith("public static class FileSystemEntry"))
{
index = i;
break;
}
if (currentLine.StartsWith("public class FileSystemEntry"))
{
index = i;
break;
}
if (currentLine.StartsWith("public abstract class FileSystemEntry"))
{
index = i;
break;
}
}
if (index < 0) continue;
into a call thus:
var index = lines.FindIndex(currentLine => /*...*/);
I need help with how to derive the proper body of the lambda expression that matches what the for loop does.
Thanks in advance!
EDIT 1
I squinted my eyes at my loop just a little more. I am looking for a predicate to use specifically with the FindIndex method. I thought a little harder and I figured out maybe I can get away with:
var index = lines.FindIndex(currentLine => !currentLine.Trim.EndsWith("Tests") && currentLine.Trim().StartsWith("public static class FileSystemEntry") || currentLine.Trim().StartsWith("public class FileSystemEntry") || currentLine.Trim().StartsWith("public abstract class FileSystemEntry"));
Perhaps I can implement an extension method
public static bool StartsWithAnyOf(this string value, params string[] testStrings)
{
var result = false;
try
{
if (string.IsNullOrWhiteSpace(value.Trim())) return result;
if (testStrings == null || !testStrings.Any()) return result;
foreach(var element in testStrings)
if (value.Trim().StartsWith(element))
{
result = true;
break;
}
}
catch
{
result = false;
}
return result;
}
Then I'd declare another method:
public static bool KeepLine(string currentLine)
{
if (string.IsNullOrWhiteSpace(currentLine.Trim())) return false;
if (currentLine.Trim().EndsWith("Tests")) return false;
return currentLine.StartsWithAnyOf(
"public static class FileSystemEntry",
"public class FileSystemEntry",
"public abstract FileSystemEntry"
);
}
Then use it thus:
var index = lines.FindIndex(KeepLine);
Would that work?
I havent tested this thoroughly but it seems to pass basic sanity if I compare to original code supplied above. Note that this is not best when it comes to measuring performance. The 'foreach' loop with anonymous function has flaw that you cannot break away from the anonymous function. The only way to come out of a foreach is to run all foreach statements. In order to preserve first index where criteria matches against line contents, I am using index in else if() comparison statement. This means the foreach loop will run for all lines despite of having the first occurrence of matching lines found.
lines.ForEach((l) =>
{
if (l.EndsWith("Tests")) ;
else if (index ==0 && (l.StartsWith("public static class FileSystemEntry") ||
l.StartsWith("public class FileSystemEntry") ||
l.StartsWith("public abstract class FileSystemEntry")))
{
index = lines.IndexOf(l);
}
});
Related
When I want to delete my specific char which is "o" in list in this code it delete some of them and others it does not delete. I mean when I debug it the result is (Roazooooor). I want to delete all of "o" chars not half of them
and when I debug it, I want it to give me (razr) without "o".
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string name = "Rooazoooooooooor";
var nameChar = new List<char>();
nameChar.AddRange(name);
for (int i = 0; i < nameChar.Count; i++)
{
if (nameChar[i] == 'o')
nameChar.Remove(nameChar[i]);
Console.Write(nameChar[i]);
}
Console.WriteLine();
}
}
}
The problem is that when you remove an item, you're then moving onto the next index, even though you'll have shuffled everything after that item up one.
In this particular case, the simplest option would be to use string.Replace first:
name = name.Replace("o", "");
var nameChar = new List<char>(name);
Or you could keep your existing code and use:
while (nameChar.Remove('o')) ;
Or
nameChar.RemoveAll(c => c == 'o');
All of these would leave you with a list without any 'o' elements.
For absolute minimal changes in your current code, you could change your loop to:
for (int i = 0; i < nameChar.Count; i++)
{
if (nameChar[i] == 'o')
{
// After removing the element at index i,
// we want to try index i again, so decrement
// and continue without printing.
nameChar.Remove(nameChar[i]);
i--;
continue;
}
Console.Write(nameChar[i]);
}
Is it necessary to use List in your case? If not then:
class Program
{
static void Main(string[] args)
{
string name = "Rooazoooooooooor";
string resultName = string.Empty;
foreach (var currentChar in name)
{
if (currentChar != 'o')
resultName += currentChar;
}
Console.Write(resultName);
Console.ReadKey();
}
}
`
I have my code for linked list here I have attached below, In this all methods are declared in a single class, now I am refactoring my code so I am moving my methods to separate class, after moving my data to separate class my data is not getting passed which so my data is not getting displayed, I think my way of passing the data through the variables to other classes is where the problem occurs, I don't know how to rectify this..here I have attached my code..
here is my code:
//My node class:
public class Node
{
public int info;
public Node link;
public Node(int i)
{
info = i;
link = null;
}
}
// My class to print the data:
public class Display
{
public void DispalyList()//list to display the items
{
Node p;
SingleLinkedList lst = new SingleLinkedList();
if (lst.start == null) // I have a doubt whether calling the start variable using that object is not correct., If data is being passed here I can proceed further
{
Console.WriteLine("List is empty");
return;
}
Console.Write("list is: ");
p = lst.start; //The way of calling the start object is wrong I guess
while (p != null) // p value is null here
{
Console.Write(p.info + " ");
p = p.link;
}
Console.WriteLine();
}
}
// My Single Linked list class://for time being i have two methods in this class
public class SingleLinkedList
{
public Node start;
public SingleLinkedList()//constructor
{
start = null;
}
public void CreateList() //Method where I am inserting the data
{
int i, n, data;//variable declaration
Console.Write("enter the number of nodes");
n = Convert.ToInt32(Console.ReadLine());
if (n == 0)
return;
for (i = 1; i <= n; i++)
{
Console.WriteLine("enter the element to be inserted");
data = Convert.ToInt32(Console.ReadLine());
InsertAtEnd(data);
}
}
public void InsertAtEnd(int data)//Method to insert the data into the list
{
Node p;
Node temp = new Node(data);
if (start == null)
{
start = temp;
return;
}
p = start;
while (p.link != null)
p = p.link;
p.link = temp;
}
}
}
//Main class using switch case:// I am just calling the display method alone for time being
public class Program
{
static void Main(string[] args)//Main program calling the methods
{
int choice;
SingleLinkedList list = new SingleLinkedList();
list.CreateList();// calling the createlist method
while (true)
{
Console.WriteLine("1.Display List");
Console.WriteLine("Enter your choice");
choice = Convert.ToInt32(Console.ReadLine());
if (choice == 13) //I have 13 cases for time being I have shown only one below
break;
switch(choice)//Switch case calling the methods
{
case 1:
Display dis = new Display();
dis.DispalyList();//calling display method which prints the data
break;
default://default case
Console.WriteLine("Wrong Choice");
}
}
}
}
Here is my above code, in which the value of the start in the[ DisplayList()] method is assigning null.If the data is being passed to it in this method i can follow the same for my other classes too. I don't know how to assign the data here..
I would suggest using ReadAllLines() instead, which will return a collection of strings, one per line in your input:
public class ReadingTextFile: IReadingTextFile {
public IEnumerable<string> content() { // change the return type
string path = # "C:\Users\s\Desktop\Datas\Data Input.txt";
var data = File.ReadAllLines(path); // and this
return data;
}
}
Then you can either just use the file.content() if all you need is a list:
IReadingTextFile file = new ReadingTextFile();
LinkedList < string > data = new LinkedList < string > ();
IEnumerable<string> inp = file.content(); // this is a collection of strings, one per line of your input
// ...
Or if you still want a linked list, you can just use the LinkedList constructor that takes a collection:
public class Readingtolist {
public void Input() {
IReadingTextFile file = new ReadingTextFile();
Stopwatch sw = new Stopwatch();
sw.Start();
IEnumerable<string> inp = file.content(); // the reading the file is probably what you really want to time.
sw.Stop();
var data = new LinkedList<string>(inp); // note the use of (inp) here
Console.Write("\n time Taken For Read Data: {0} ms",
sw.Elapsed.TotalMilliseconds);
Console.WriteLine("\n The items are{0}", inp);
}
}
I have to convert word to html which I'm doing with Aspose and that is working well. The problem is that it is producing some redundant elements which I think is due to the way the text is store in word.
For example in my word document the text below appears:
AUTHORIZATION FOR RELEASE
When converted to html it becomes:
<span style="font-size:9pt">A</span>
<span style="font-size:9pt">UTHORIZATION FOR R</span>
<span style="font-size:9pt">ELEASE</span>
I'm using C# and would like a way to remove the redundant span elements. I'm thinking either AngleSharp or html-agility-pack should be able to do this but I'm not sure this is the best way?
What I wound up doing is iterating over all the elements and when adjacent span elements were detected I concatenated the text together. Here is some code if others run into this issue. Note code could use some cleanup.
static void CombineRedundantSpans(IElement parent)
{
if (parent != null)
{
if (parent.Children.Length > 1)
{
var children = parent.Children.ToArray();
var previousSibling = children[0];
for (int i = 1; i < children.Length; i++)
{
var current = children[i];
if (previousSibling is IHtmlSpanElement && current is IHtmlSpanElement)
{
if (IsSpanMatch((IHtmlSpanElement)previousSibling, (IHtmlSpanElement)current))
{
previousSibling.TextContent = previousSibling.TextContent + current.TextContent;
current.Remove();
}
else
previousSibling = current;
}
else
previousSibling = current;
}
}
foreach(var child in parent.Children)
{
CombineRedundantSpans(child);
}
}
}
static bool IsSpanMatch(IHtmlSpanElement first, IHtmlSpanElement second)
{
if (first.ChildElementCount < 2 && first.Attributes.Length == second.Attributes.Length)
{
foreach (var a in first.Attributes)
{
if (second.Attributes.Count(t => t.Equals(a)) == 0)
{
return false;
}
}
return true;
}
return false;
}
Here is some dummy code that illustrates what I want to do:
List<int> list1 = new List<int>();
//Code to fill the list
foreach(int number in list1)
{
if(number%5==0)
{
list1.Remove(number);
}
}
Assuming the test actually removes an int, it will throw an error. Is there a way of doing this in a foreach, or do I have to convert it to a for loop?
You can't remove items from a collection that you are iterating thru with a for each.
I would do this...
list1 = list1.Where(l => l % 5 != 0).ToList();
The RemoveAll() method comes closest to what you want, I think:
list1.RemoveAll(i => i%5 == 0);
Actually if you want to remove the list as you state in the O.P you could do:
List<int> list1 = new List<int>();
//Code to fill the list
for(var n = 0; n < list.Count; i++)
{
if (list[n] % 5 == 0)
{
list1.Remove(list[n--]);
}
}
Edited to Add
The reason why you can't change a list while in a for each loos is as follows:
[Serializable()]
public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
{
private List<T> list;
private int index;
private int version;
private T current;
internal Enumerator(List<T> list) {
this.list = list;
index = 0;
version = list._version;
current = default(T);
}
public void Dispose() {
}
public bool MoveNext() {
List<T> localList = list;
if (version == localList._version && ((uint)index < (uint)localList._size))
{
current = localList._items[index];
index++;
return true;
}
return MoveNextRare();
}
private bool MoveNextRare()
{
if (version != list._version) {
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = list._size + 1;
current = default(T);
return false;
}
public T Current {
get {
return current;
}
}
Object System.Collections.IEnumerator.Current {
get {
if( index == 0 || index == list._size + 1) {
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
}
return Current;
}
}
void System.Collections.IEnumerator.Reset() {
if (version != list._version) {
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = 0;
current = default(T);
}
}
As far as I know, a collection cannot be modified while in a foreach loop. You need to change it to a for loop. Another way you can accomplish that is using LINQ.
You can't do it in-situ using foreach, because it invalidates the enumerator.
Either take a copy of the list and iterate over that, or use a different type of loop such as a for() loop.
You cannot modify the collection your are enumerating through using foreach. What I often do is use a for loop and go backwards through the collection, thus you can safely remove items since the length won't be affected until you move to the previous item.
Given n enumerables of the same type that return distinct elements in ascending order, for example:
IEnumerable<char> s1 = "adhjlstxyz";
IEnumerable<char> s2 = "bdeijmnpsz";
IEnumerable<char> s3 = "dejlnopsvw";
I want to efficiently find all values that are elements of all enumerables:
IEnumerable<char> sx = Intersect(new[] { s1, s2, s3 });
Debug.Assert(sx.SequenceEqual("djs"));
"Efficiently" here means that
the input enumerables should each be enumerated only once,
the elements of the input enumerables should be retrieved only when needed, and
the algorithm should not recursively enumerate its own output.
I need some hints how to approach a solution.
Here is my (naive) attempt so far:
static IEnumerable<T> Intersect<T>(IEnumerable<T>[] enums)
{
return enums[0].Intersect(
enums.Length == 2 ? enums[1] : Intersect(enums.Skip(1).ToArray()));
}
Enumerable.Intersect collects the first enumerable into a HashSet, then enumerates the second enumerable and yields all matching elements.
Intersect then recursively intersects the result with the next enumerable.
This obviously isn't very efficient (it doesn't meet the constraints). And it doesn't exploit the fact that the elements are sorted at all.
Here is my attempt to intersect two enumerables. Maybe it can be generalized for n enumerables?
static IEnumerable<T> Intersect<T>(IEnumerable<T> first, IEnumerable<T> second)
{
using (var left = first.GetEnumerator())
using (var right = second.GetEnumerator())
{
var leftHasNext = left.MoveNext();
var rightHasNext = right.MoveNext();
var comparer = Comparer<T>.Default;
while (leftHasNext && rightHasNext)
{
switch (Math.Sign(comparer.Compare(left.Current, right.Current)))
{
case -1:
leftHasNext = left.MoveNext();
break;
case 0:
yield return left.Current;
leftHasNext = left.MoveNext();
rightHasNext = right.MoveNext();
break;
case 1:
rightHasNext = right.MoveNext();
break;
}
}
}
}
OK; more complex answer:
public static IEnumerable<T> Intersect<T>(params IEnumerable<T>[] enums) {
return Intersect<T>(null, enums);
}
public static IEnumerable<T> Intersect<T>(IComparer<T> comparer, params IEnumerable<T>[] enums) {
if(enums == null) throw new ArgumentNullException("enums");
if(enums.Length == 0) return Enumerable.Empty<T>();
if(enums.Length == 1) return enums[0];
if(comparer == null) comparer = Comparer<T>.Default;
return IntersectImpl(comparer, enums);
}
public static IEnumerable<T> IntersectImpl<T>(IComparer<T> comparer, IEnumerable<T>[] enums) {
IEnumerator<T>[] iters = new IEnumerator<T>[enums.Length];
try {
// create iterators and move as far as the first item
for (int i = 0; i < enums.Length; i++) {
if(!(iters[i] = enums[i].GetEnumerator()).MoveNext()) {
yield break; // no data for one of the iterators
}
}
bool first = true;
T lastValue = default(T);
do { // get the next item from the first sequence
T value = iters[0].Current;
if (!first && comparer.Compare(value, lastValue) == 0) continue; // dup in first source
bool allTrue = true;
for (int i = 1; i < iters.Length; i++) {
var iter = iters[i];
// if any sequence isn't there yet, progress it; if any sequence
// ends, we're all done
while (comparer.Compare(iter.Current, value) < 0) {
if (!iter.MoveNext()) goto alldone; // nasty, but
}
// if any sequence is now **past** value, then short-circuit
if (comparer.Compare(iter.Current, value) > 0) {
allTrue = false;
break;
}
}
// so all sequences have this value
if (allTrue) yield return value;
first = false;
lastValue = value;
} while (iters[0].MoveNext());
alldone:
;
} finally { // clean up all iterators
for (int i = 0; i < iters.Length; i++) {
if (iters[i] != null) {
try { iters[i].Dispose(); }
catch { }
}
}
}
}
You can use LINQ:
public static IEnumerable<T> Intersect<T>(IEnumerable<IEnumerable<T>> enums) {
using (var iter = enums.GetEnumerator()) {
IEnumerable<T> result;
if (iter.MoveNext()) {
result = iter.Current;
while (iter.MoveNext()) {
result = result.Intersect(iter.Current);
}
} else {
result = Enumerable.Empty<T>();
}
return result;
}
}
This would be simple, although it does build the hash-set multiple times; advancing all n at once (to take advantage of sorted) would be hard, but you could also build a single hash-set and remove missing things?