patterncsharpMinor
Enumerating text (ranges) vertically and horizontally
Viewed 0 times
texthorizontallyenumeratingrangesverticallyand
Problem
I'm writing a parser and I need to iterate through text vertically (down to bottom) and horizontally (left to right).
This alone would be easy but some text parts need to be rescanned by another parser and for this I need to reiterate over random lines or a text range.
I wrote two enumerators.
One for enumerating all lines or only some of them:
and another one for enumerating chars or only a particular range:
```
internal class CharEnumerator
{
private const int IndexOutOfRange = -1;
public CharEnumerator(string line) : this(line, 0, line.Length) { }
public CharEnumerator(string line, int index0, int length)
{
Line = line;
Index0 = index0;
Length = length;
Index = IndexOutOfRange;
}
public string Line { get; }
public int Index0 { get; }
public int Length { get; }
public int Counter { get; private set; }
public int Index { get; private set; }
public bool IsIndexInRange => Index >= Index0 && Counter IsIndexInRange ? Line[Index] : '\0';
public bool MoveNext()
{
if (!IsIndexInRange)
{
Index = Index0;
Counter++;
return true;
}
Index++;
Counter++;
return (Counter <= Length);
This alone would be easy but some text parts need to be rescanned by another parser and for this I need to reiterate over random lines or a text range.
I wrote two enumerators.
One for enumerating all lines or only some of them:
internal class LineEnumerator
{
private const int IndexOutOfRange = -1;
public LineEnumerator(IReadOnlyList lines) : this(lines, null) { }
public LineEnumerator(IReadOnlyList lines, IReadOnlyList lineNumbers)
{
Lines = lines;
Index = -1;
LineNumbers = lineNumbers;
}
public IReadOnlyList Lines { get; }
public IReadOnlyList LineNumbers { get; }
public int Index { get; private set; }
public bool IsIndexInRange => Index > IndexOutOfRange && Index IsIndexInRange ? Lines[LineNumber] : null;
public CharEnumerator Chars => IsIndexInRange ? new CharEnumerator(Current) : null;
public bool MoveNext() => ++Index < (LineNumbers?.Count ?? Lines.Count);
}and another one for enumerating chars or only a particular range:
```
internal class CharEnumerator
{
private const int IndexOutOfRange = -1;
public CharEnumerator(string line) : this(line, 0, line.Length) { }
public CharEnumerator(string line, int index0, int length)
{
Line = line;
Index0 = index0;
Length = length;
Index = IndexOutOfRange;
}
public string Line { get; }
public int Index0 { get; }
public int Length { get; }
public int Counter { get; private set; }
public int Index { get; private set; }
public bool IsIndexInRange => Index >= Index0 && Counter IsIndexInRange ? Line[Index] : '\0';
public bool MoveNext()
{
if (!IsIndexInRange)
{
Index = Index0;
Counter++;
return true;
}
Index++;
Counter++;
return (Counter <= Length);
Solution
I would go with standard
Where
And:
And:
IEnumerator and IEnumerable, so it could be consumed as:static void Main(string[] args)
{
var template = new Template("foo", "bar", "baz", "qux");
foreach (var row in template)
Console.WriteLine(row.Text);
Console.WriteLine();
foreach (var row in template.Select(1, 3))
Console.WriteLine(row.Text);
Console.WriteLine();
foreach (var row in template.Select(0))
foreach (var column in row)
Console.WriteLine(column.Text);
Console.WriteLine();
foreach (var row in template.Select(0))
foreach (var column in row.Range(0,3))
Console.WriteLine(column.Text);
}Where
Select allows to subset by indecies provided, while Range is working in terms of start and count. Row and Column objects have Index (current selection) and Indecies (allowed to be selected) properties:public class Template : IEnumerable
{
public Template(params string[] lines)
{
Lines = lines;
}
public IEnumerable Range(int start, int count) => Select(Enumerable.Range(start, count));
public IEnumerable Select(params int[] indecies) => Select(indecies as IEnumerable);
public IEnumerable Select(IEnumerable indecies)
{
foreach (var index in indecies)
yield return new Row(Lines.ElementAtOrDefault(index), index, indecies);
}
public IEnumerator GetEnumerator() => Range(0, Lines.Length).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
string[] Lines { get; }
}And:
public struct Row : IEnumerable
{
public Row(string text, int index, IEnumerable indecies)
{
Text = text;
Index = index;
Indecies = indecies;
}
public string Text { get; }
public int Index { get; }
public IEnumerable Indecies { get; }
public IEnumerable Range(int start, int count) => Select(Enumerable.Range(start, count));
public IEnumerable Select(params int[] indecies) => Select(indecies as IEnumerable);
public IEnumerable Select(IEnumerable indecies)
{
foreach (var index in indecies)
yield return new Column(Text.ElementAtOrDefault(index), index, indecies);
}
public IEnumerator GetEnumerator() => Range(0, Text.Length).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}And:
public struct Column
{
public Column(char text, int index, IEnumerable indecies)
{
Text = text;
Index = index;
Indecies = indecies;
}
public char Text { get; }
public int Index { get; }
public IEnumerable Indecies { get; }
}Code Snippets
static void Main(string[] args)
{
var template = new Template("foo", "bar", "baz", "qux");
foreach (var row in template)
Console.WriteLine(row.Text);
Console.WriteLine();
foreach (var row in template.Select(1, 3))
Console.WriteLine(row.Text);
Console.WriteLine();
foreach (var row in template.Select(0))
foreach (var column in row)
Console.WriteLine(column.Text);
Console.WriteLine();
foreach (var row in template.Select(0))
foreach (var column in row.Range(0,3))
Console.WriteLine(column.Text);
}public class Template : IEnumerable<Row>
{
public Template(params string[] lines)
{
Lines = lines;
}
public IEnumerable<Row> Range(int start, int count) => Select(Enumerable.Range(start, count));
public IEnumerable<Row> Select(params int[] indecies) => Select(indecies as IEnumerable<int>);
public IEnumerable<Row> Select(IEnumerable<int> indecies)
{
foreach (var index in indecies)
yield return new Row(Lines.ElementAtOrDefault(index), index, indecies);
}
public IEnumerator<Row> GetEnumerator() => Range(0, Lines.Length).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
string[] Lines { get; }
}public struct Row : IEnumerable<Column>
{
public Row(string text, int index, IEnumerable<int> indecies)
{
Text = text;
Index = index;
Indecies = indecies;
}
public string Text { get; }
public int Index { get; }
public IEnumerable<int> Indecies { get; }
public IEnumerable<Column> Range(int start, int count) => Select(Enumerable.Range(start, count));
public IEnumerable<Column> Select(params int[] indecies) => Select(indecies as IEnumerable<int>);
public IEnumerable<Column> Select(IEnumerable<int> indecies)
{
foreach (var index in indecies)
yield return new Column(Text.ElementAtOrDefault(index), index, indecies);
}
public IEnumerator<Column> GetEnumerator() => Range(0, Text.Length).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}public struct Column
{
public Column(char text, int index, IEnumerable<int> indecies)
{
Text = text;
Index = index;
Indecies = indecies;
}
public char Text { get; }
public int Index { get; }
public IEnumerable<int> Indecies { get; }
}Context
StackExchange Code Review Q#138061, answer score: 3
Revisions (0)
No revisions yet.