Related
I try to 'linearize' every possibilities of a binary and-or tree to make it more easily readable. Every possibilities should be added to the following structure :
// (x1 AND x2) OR (x2 AND x3)
List<List<Node>> possibilities = new List<List<Node>>() {
{ x1, x2 },
{ x2, x3 }
};
I'm facing some difficulties to generate the list-based possibilities from a tree structure. A simplified version or my algorithm which doesn't return a correct answer in many case is:
class TreeDecomposer {
public List<TreePath> Possibilities = new List<TreePath>();
// TreePath = { List<TreeNode> path, bool IsAdded }
public TreeDecomposer(AbstractTree tree) {
DecomposeTree(tree, new TreePath());
}
public void DecomposeTree(AbstractTree tree, TreePath path)
{
// Add the path to the list of possibilities
if (!path.IsAdded)
{
Possibilities.Add(path);
path.IsAdded = true;
}
// Recursive browse
if (tree is TreeConnector) {
TreeConnector treeConnector = (TreeConnector)tree;
if (treeConnector.Connection == "&")
{
DecomposeTree(treeConnector.LeftTree, path);
DecomposeTree(treeConnector.RightTree, path);
}
else if (treeConnector.Connection == "|")
{
TreePath clonedPath = (TreePath)path.Clone(); // deep clone
DecomposeTree(treeConnector.LeftTree, path);
DecomposeTree(treeConnector.RightTree, clonedPath); // somehow 'or' operator multiplies possibilities by two?
}
}
// Leaf
else if (tree is TreeValue) {
TreeValue treeValue = (TreeValue)tree;
path.Add(treeValue);
}
}
}
I need help to find the correct algorithm working with my tree structure to browse the tree and construct every possibitilies of 'AND-path'.
Two basic example:
Binary end-or tree example (1)
Formula: (a | b) & (c | d)
Possibilities:
{
{a, c}, // or {c, a}, the order doesn't matter
{a, d},
{b, c},
{b, d}
}
Binary end-or tree example (2)
Formula: a & ((b | c) & d)
Possibilities:
{
{a, b, d}, // or {d, b, a}, the order doesn't matter
{a, c, d}
}
Tree structure:
The implementation or the Tree structure is the following:
abstract class AbstractTree {}
class TreeConnector: AbstractTree
{
public string Connection; // '&' or '|'
public AbstractTree LeftTree;
public AbstractTree RightTree;
}
class TreeValue : AbstractTree
{
public string Data; // 'a', or 'b', ...
}
Thanks a lot for your help.
Based on #Freggar link, here is a simplified implementation of the 'OR' distribution. It's probably not done in the most efficient way, but it gives a global idea of what I was looking for.
/*
TreePath = {
HashSet<TreeNode> path,
bool IsAdded // set to false even if it's true when an instance is cloned
}
Every class (Tree...) define the methods:
public object Clone()
public bool Equals(T typedObj)
public override bool Equals(object obj)
public override int GetHashCode()
*/
enum TreeBranch
{
Unknown,
Left,
Right
}
class TreeDecomposer {
public List<TreePath> Possibilities = new List<TreePath>();
public TreeDecomposer(AbstractTree tree)
{
DecomposeTree(null, tree, TreeBranch.Unknown, new TreePath());
RemoveDuplicatePaths();
}
public void DecomposeTree(AbstractTree parentNode, AbstractTree node, TreeBranch branch, TreePath path)
{
if (!path.IsAdded)
{
Possibilities.Add(path);
path.IsAdded = true;
}
// Recursive browse
if (node is TreeConnector) {
TreeConnector treeConnector = (TreeConnector)node;
if (treeConnector.Connection == "&")
{
DecomposeTree(treeConnector, treeConnector.LeftTree, TreeBranch.Left, path);
DecomposeTree(treeConnector, treeConnector.RightTree, TreeBranch.Right, path);
}
else if (treeConnector.Connection == "|")
{
// In this case, parentNode is a TreeOperator
if (parentNode != null)
{
// Left distribution
TreePath clonedPathLeftDistribution = (TreePath)path.Clone();
TreeConnector parentTreeConnectorLeftDistribution = (TreeConnector)parentNode.Clone();
// Right distribution
TreePath clonedPathRightDistribution = (TreePath)path.Clone();
TreeConnector parentTreeConnectorRightDistribution = (TreeConnector)parentNode.Clone();
if (branch == TreeBranch.Left)
{
parentTreeConnectorLeftDistribution.LeftTree = treeConnector.LeftTree;
parentTreeConnectorRightDistribution.LeftTree = treeConnector.RightTree;
}
else if (branch == TreeBranch.Right)
{
parentTreeConnectorLeftDistribution.RightTree = treeConnector.LeftTree;
parentTreeConnectorRightDistribution.RightTree = treeConnector.RightTree;
}
// Remove obsolete path
Possibilities.Remove(path);
// Browse recursively distributed tree ; the path must be different (by ref) if the parent operator is 'OR'
DecomposeTree(
parentTreeConnectorLeftDistribution,
parentTreeConnectorLeftDistribution.LeftTree,
TreeBranch.Left,
parentTreeConnectorLeftDistribution.Connection == "|"
? (TreePath)clonedPathLeftDistribution.Clone()
: clonedPathLeftDistribution
);
DecomposeTree(
parentTreeConnectorLeftDistribution,
parentTreeConnectorLeftDistribution.RightTree,
TreeBranch.Right,
clonedPathLeftDistribution
);
DecomposeTree(
parentTreeConnectorRightDistribution,
parentTreeConnectorRightDistribution.LeftTree,
TreeBranch.Left,
parentTreeConnectorLeftDistribution.Connection == "|"
? (TreePath)clonedPathRightDistribution.Clone()
: clonedPathRightDistribution
);
DecomposeTree(
parentTreeConnectorRightDistribution,
parentTreeConnectorRightDistribution.RightTree,
TreeBranch.Right,
clonedPathRightDistribution
);
}
// The operator is the root of the tree; we simply divide the path
else
{
TreePath clonedLeftPath = (TreePath)path.Clone();
TreePath clonedRightPath = (TreePath)path.Clone();
// Remove obsolete path
Possibilities.Remove(path);
DecomposeTree(treeConnector, treeConnector.LeftTree, TreeBranch.Left, clonedLeftPath);
DecomposeTree(treeConnector, treeConnector.RightTree, TreeBranch.Right, clonedRightPath);
}
}
break;
}
// Leaf
else if (node is TreeValue) {
TreeValue treeValue = (TreeValue)node;
path.Add(treeValue);
}
}
public void RemoveDuplicatePaths()
{
Possibilities = Possibilities.Distinct().ToList();
}
}
Note:
Here, I want to keep only the unique possibilities; that's why I use HashSet instead of List:
"a & a & b" => "a & b"
The method RemoveDuplicatePaths is used to remove duplicated combinations:
"a & b" and "b & a" are both the same possibility (regarding the truth value)
I have strings which come from resources in exponential form like the following: 2⁴. I was wondering if there is a way to split this into:
var base = 2; //or even "2", this is also helpful since it can be parsed
and
var exponent = 4;
I have searched the internet and msdn Standard Numeric Format Strings also, but I was unable to find the solve for this case.
You can add mapping between digits to superscript digits, then select all digits from source (this will be the base) and all the others - the exponent
const string superscriptDigits = "⁰¹²³⁴⁵⁶⁷⁸⁹";
var digitToSuperscriptMapping = superscriptDigits.Select((c, i) => new { c, i })
.ToDictionary(item => item.c, item => item.i.ToString());
const string source = "23⁴⁴";
var baseString = new string(source.TakeWhile(char.IsDigit).ToArray());
var exponentString = string.Concat(source.SkipWhile(char.IsDigit).Select(c => digitToSuperscriptMapping[c]));
Now you can convert base and exponent to int.
Also you'll need to validate input before executing conversion code.
Or even without mapping:
var baseString = new string(source.TakeWhile(char.IsDigit).ToArray());
var exponentString = string.Concat(source.SkipWhile(char.IsDigit).Select(c => char.GetNumericValue(c).ToString()));
You can use a regular expression together with String.Normalize:
var value = "42⁴³";
var match = Regex.Match(value, #"(?<base>\d+)(?<exponent>[⁰¹²³⁴-⁹]+)");
var #base = int.Parse(match.Groups["base"].Value);
var exponent = int.Parse(match.Groups["exponent"].Value.Normalize(NormalizationForm.FormKD));
Console.WriteLine($"base: {#base}, exponent: {exponent}");
The way your exponent is formatted is called superscript in English.
You can find many question related to this if you search with that keyword.
Digits in superscript are mapped in Unicode as:
0 -> \u2070
1 -> \u00b9
2 -> \u00b2
3 -> \u00b3
4 -> \u2074
5 -> \u2075
6 -> \u2076
7 -> \u2077
8 -> \u2078
9 -> \u2079
You can search for that values in your string:
Lis<char> superscriptDigits = new List<char>(){
'\u2070', \u00b9', \u00b2', \u00b3', \u2074',
\u2075', \u2076', \u2077', \u2078', \u2079"};
//the rest of the string is the expontent. Join remaining chars.
str.SkipWhile( ch => !superscriptDigits.Contains(ch) );
You get the idea
You can use a simple regex (if your source is quite clean):
string value = "2⁴⁴";
string regex = #"(?<base>\d+)(?<exp>.*)";
var matches = Regex.Matches(value, regex);
int b;
int exp = 0;
int.TryParse(matches[0].Groups["base"].Value, out b);
foreach (char c in matches[0].Groups["exp"].Value)
{
exp = exp * 10 + expToInt(c.ToString());
}
Console.Out.WriteLine("base is : {0}, exponent is {1}", b, exp);
And expToInt (based on Unicode subscripts and superscripts):
public static int expToInt(string c)
{
switch (c)
{
case "\u2070":
return 0;
case "\u00b9":
return 1;
case "\u00b2":
return 2;
case "\u00b3":
return 3;
case "\u2074":
return 4;
case "\u2075":
return 5;
case "\u2076":
return 6;
case "\u2077":
return 7;
case "\u2078":
return 8;
case "\u2079":
return 9;
default:
throw new ArgumentOutOfRangeException();
}
}
This will output:
base is 2, exp is 44
So I'm trying to make BFS algorithm, and I was able to calculate the shortest path distance between each 2 nodes. But the neighbors of each node (i.e node A) is not only nodes it's a dictionary of nodes as a key and a hashset of matches where each two nodes played in. Now, I don't know how to store the path while the BFS is working... This the adjacency function that returns the neighbors of each nodes
Dictionary<string, Dictionary<string, HashSet<string>>> dic= new Dictionary<string, Dictionary < string,, HashSet < string >>>;
public IEnumerable<KeyValuePair<string, HashSet<string>>> adjacentto(string v)
{
return dic[v];
}
And this is my BFS function:
private Dictionary<string, int> dist = new Dictionary<string, int>();
public void BFSDegree(Graph g, string s, string p)
{
Queue<string> q = new Queue<string>();
dist.Add(s, 0);
q.Enqueue(s);
while (q.Count() != 0)
{
string j = q.Dequeue();
//count = 0;
foreach (KeyValuePair<string, HashSet<string>> h in g.adjacentto(j))
{
if (!dist.ContainsKey(h.Key))
{
q.Enqueue(h.Key);
dist.Add(h.Key, 1 + dist[j]);
}
if (j == p)
{
Console.WriteLine(" " + dist[j]);
return;
}
}
}
}
So, what I need it is to go see the path and read the values of the hashset, for example node A and node B played together in 3 matches match 1, match 2, match 7 so this should be the path. So I'm gonna print to the console "The path is either match 1, match 2 or match 7). And the same thing if I had 2 nodes that didn't star in a match together but they both starred with node A in 2 separate matches so the path should be through either of these 2 matches. How do I keep track of the paths and store the path while operating BFS? This is the file I'm reading the graph from.
This is the how I built my graph
And this is what I want to achieve
I was able to achieve the first goal (degree) through using BFS. But now I don't know how to achieve the "chain" or the path. The chain is nothing but the number of the movies in the path so I think if I'm able to save the path (show the path) while the BFS is working I shall be able to achieve the chain. So my problem is the last goal how do I save the path and show it.
In order to find the shortest path from one node to another, you can keep track of parents of each node. for example the graph below, when I run bfs from 0 up to 9, I keep track of each node reached an assign a parent. Once a parent is assigned, I do not reassign. So for example, if I want to find path from 0 to 9 and length I simply backtrack i.e. 9-7-3-1-0 so start from 9's parent and check 7's parent and so forth until you get to start node. We can easily then get the length.
As for the query, when you do something like C/E you can first run bfs to check for the path "which should be 2 going from C-A-E" and of course there could be other paths but I guess shortest path is the best for what you want ?
Anyway, let's assume we choose path C-A-E we can update the Rel. by the number of edges so Rel = 2 and then Chain will be
//Nodes -> [C, A, E]
//Parents ->[C, C, A]
Start from E // which is destination
Get movies of parent // parent is A, returns Movies 2
move to parent // now we are checking A
Get movies of parent // parent is C, returns Movies 1 Movies 7
break;
You break as soon as you reach source or you can do it vice versa
At the end you have Movies 2,1, and 7
A parent is just a predecessor of a node. For example, while you run Bfs if you go from 0 to 1 then 0 is parent of 1
Here is an implementation that I hope will help you understand it a little better.
private Map<Integer, List<Integer>> _adjacencyList;
private Map<Integer, String> _movies; // will store neighbors here
private Queue<Integer> queue;
private int [] visited;
public BaconNumber(int V){// v here is number of vertices
_adjacencyList = new HashMap<Integer, List<Integer>>();
_movies = new HashMap<Integer, String>();
queue = new LinkedList<Integer>();
visited = new int[V];
for(int i = 0; i < V; i++){
_adjacencyList.put(i, new LinkedList<Integer>());// add a linkedlist for each vertex
visited[i] = -1;
}
}
Fill movies here
private void fillNeighbors(){
//0 = A, 1 = B, 2 = C, 3 = D, 4 = E
_movies.put(0, "Z Movie 0 | B Movie 1 Movie 2 Movie 7 | C Movie 1 Movie 7 | D Movie 2 Movie 7 | E Movie 2");
_movies.put(1, "A Movie 1 Movie 2 Movie 7 | C Movie 1 Movie 7 | D Movie 2 Movie 7 | E Movie 2");
_movies.put(2, "A Movie 1 Movie 7 | B Movie 1 Movie 7 | D Movie 7");
_movies.put(3, "E Movie 2 | A Movie 2 Movie 7 | B Movie 2 Movie 7 | C Movie 7");
_movies.put(4, "D Movie 2 | A Movie 2 | B Movie 2 | F Movie 3 | G Movie 3");
}
Get movies. This takes in two parameters. One for where we get the movie from and the other for the node we are looking for. Note that I converted the second parameter to a letter so it looks like what you have
public String getMovies(int s, int v){
String result = "";
// just getting corresponding character
int rem = v % 26;
char l = (char)((int)'A' + rem);
//System.out.println("this is char "+l);
String movie = _movies.get(s);
String [] tokens = movie.split("\\|");
for(int i = 0; i < tokens.length; i++){
String next = tokens[i];
if(next.contains(String.valueOf(l))){
result = next;
break;
}
}
return result;
}
And now the query part
String query(int source, int dest){
List<Integer> nodes = new LinkedList<Integer>();
int i, element;
visited[source] = source;
queue.add(source);
while(!queue.isEmpty()){
element = queue.remove();
i = element;
if(i == dest) break; // we stop as soon as we reach destination
nodes.add(element);
List<Integer> iList = getEdge(i);
System.out.println(i+" -> "+iList);
int x = 0;
while(x < iList.size()){
int index = iList.get(x);
if(visited[index] == -1){
queue.add(index);
visited[index] = i;
}
x++;
}
}
String result = "";
for(int x = dest; x >= 0; x= visited[x]){
if(x == source) break; // we are done
if(visited[x] != -1){
result += getMovies(x,visited[x]); // get predecessor of x movies from x
}
}
return result;
}
I need to a method (or 2?) that add suffixes to a string.
Let's say I have the string "Hello".
If I click option 1 it should create a list of strings such as
Hello a
Hello b
Hello c
I've got that part covered.
The next option I'd need it to create a list such as
Hello aa
Hello ab
Hello ac
...
Hello ba
Hello bb
Hello bc
and so on....
Also...each option has 2 other options..
Say I want to add suffix 1 as a-z and suffix 2 as 0-9
Then it'd be
Hello a0
Hello a1
Is there anyone that can help me? This is how I do a single letter increment.
if (ChkSuffix.Checked)
{
if (CmbSuffixSingle.Text == #"a - z" && CmbSuffixDouble.Text == "")
{
var p = 'a';
for (var i = 0; i <= 25; i++)
{
var keyword = TxtKeyword.Text + " " + p;
terms.Add(keyword);
p++;
//Console.WriteLine(keyword);
}
}
}
Try using these extension methods:
public static IEnumerable<string> AppendSuffix(
this string #this, string dictionary)
{
return dictionary.Select(x => #this + x);
}
public static IEnumerable<string> AppendSuffix(
this string #this, string dictionary, int levels)
{
var r = #this.AppendSuffix(dictionary);
if (levels > 1)
{
r = r.SelectMany(x => x.AppendSuffix(dictionary, levels - 1));
}
return r;
}
public static IEnumerable<string> AppendSuffix(
this IEnumerable<string> #this, string dictionary)
{
return #this.SelectMany(x => x.AppendSuffix(dictionary));
}
public static IEnumerable<string> AppendSuffix(
this IEnumerable<string> #this, string dictionary, int levels)
{
var r = #this.AppendSuffix(dictionary);
if (levels > 1)
{
r = r.SelectMany(x => x.AppendSuffix(dictionary, levels - 1));
}
return r;
}
Then call them like this:
"Hello ".AppendSuffix("abc"); // Hello a, Hello b, Hello c
"Hello ".AppendSuffix("abc", 2); // Hello aa to Hello cc
"Hello "
.AppendSuffix("abc")
.AppendSuffix("0123456789"); // Hello a0 to Hello c9
Here is the text file that I am loading in:
ORIGINAL FILE
10 BARE PCB
20 T C40
B C5, C45, C48
30 B C25
40 B C17, C18
50 B C15
60 T C20, C23,
B C6, C7, C8, C9, C10, C11, C12,
C31, C32, C33, C34, C35, C36,
C37, C38, C39
70 T C16, C21, C22, C24, C41, C42
B C3, C19, C43, C44, C47
80 T C13, C14
B C1, C2
90 B C26, C27, C28, C29, C30
100 T R65
110 T R35
120 T R34
130 T R33
140 T R21, R22, R29, R30
150 B R28, R31, R32, R37
160 T R17, R19, R26, R47, R50, R51,
R53, R57, R58, R59
B R18, R25, R42, R48, R49, R52,
R54, R55, R56, R60
170 T R23
B R10, R43
180 T R8, R9
190 T R13, R14, R15, R61, R62, R63,
R64
200 T R27, R38, R39, R40, R41
B R2, R3, R11, R44, R45, R46
210 B R1
220 T C4
230 T D1
240 T U1
250 B U10
270 B U6
280 B U5
290 B U4
300 T U2
310 B U7
320 B U8
330 T U9
340 B L2, L3
350 B L8, L9
360 B L1, L4, L5, L6, L7, L10
370 B J1, J2
380 B J3
390 B X1
400 T X2
410 B J4
420 B J5
422 B U3
2000 T TRACKING LABEL
423 ADHESIVE
424 ACCELERATOR
425 ADHESIVE
And this is what I have the file being formatted to:
FORMATTED FILE
0010 BARE PCB
0020 C40
0020A C5, C45, C48
0030A C25
0040A C17, C18
0050A C15
0060 C20, C23,
0060A C6, C7, C8, C9, C10, C11, C12,C31, C32, C33, C34, C35, C36,C37, C38, C39
0070 C16, C21, C22, C24, C41, C42
0070A C3, C19, C43, C44, C47
0080 C13, C14
0080A C1, C2
0090A C26, C27, C28, C29, C30
0100 R65
0110 R35
0120 R34
0130 R33
0140 R21, R22, R29, R30
0150A R28, R31, R32, R37
0160 R17, R19, R26, R47, R50, R51,R53, R57, R58, R59
0160A R18, R25, R42, R48, R49, R52,R54, R55, R56, R60
0170 R23
0170A R10, R43
0180 R8, R9
0190 R13, R14, R15, R61, R62, R63,R64
0200 R27, R38, R39, R40, R41
0200A R2, R3, R11, R44, R45, R46
0210A R1
0220 C4
0230 D1
0240 U1
0250A U10
0270A U6
0280A U5
0290A U4
0300 U2
0310A U7
0320A U8
0330 U9
0340A L2, L3
0350A L8, L9
0360A L1, L4, L5, L6, L7, L10
0370A J1, J2
0380A J3
0390A X1
0400 X2
0410A J4
0420A J5
0422A U3
2000 TRACKING LABEL
0423 ADHESIVE
0424 ACCELERATOR
0425 ADHESIVE
HOWEVER
If you look closely in the formatted file there is a few spots where there is no space after one of the commas (C12,C31 & C36,C37 & R51,R53 & R52,R54 & R63,R64). I would like there to be a space in between there..
CODE
private void openRefsFormatHelper()
{
try
{
// Resets the formatted refs text.
formattedRefsTextRichTextBox.ResetText();
// Reads the lines in the file to format.
var reader = File.OpenText(openRefs.FileName);
// Creates a list for the lines to be stored in.
var list = new List<string>();
// Adds each line in the file to the list.
while (true)
{
var line = reader.ReadLine();
if (line == null)
break;
list.Add(line);
}
// Handles all of the requirements for the reference text.
list = fourDigitRequirement(list);
list = concatRequirement(list);
list = startsWithBRequirement(list);
list = elementIsBRequirement(list);
list = removeTRequirement(list);
// Prints the formatted refs to the richtextbox.
foreach (var line in list)
formattedRefsTextRichTextBox.AppendText(line + "\n");
}
// Catches an exception if the file could not be formatted.
catch (Exception)
{
MessageBox.Show("There was a problem formatting the 'Refs File'.", "Refs File Format Error",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
static List<string> elementIsBRequirement(List<string> list)
{
// Creates a new list to return with new format.
var result = new List<string>();
// Checks each line in the list.
foreach (var line in list)
{
// Splits each line into 'parts'
var parts = line.Split(' ');
// Checks if the second 'part' array is "B"
if (parts[1].Equals("B"))
{
// If it is "B", replace with "A" and add to the new list "result"
parts[0] += "A";
parts[1] = string.Empty;
result.Add(string.Join(" ", parts));
}
// Otherwise keep the line how it is.
else
result.Add(line);
}
// Returns the new list so it can be formatted further.
return result;
}
static List<string> startsWithBRequirement(List<string> list)
{
// Creates a new list to return with new format.
var result = new List<string>();
var i = 0;
// Checks if the line begins with "B"
foreach (var line in list)
{
// Splits each line into 'parts'
var parts = line.Split(' ');
// Checks if the first 'part' array is "B"
if (parts[0].Equals("B"))
{
// If it is "B", copy the previous line down and add "A" where "B" was at
// and add to the new list "result".
parts[0] = string.Empty;
result.Add(list[i - 1].Split(' ')[0] + "A" + string.Join(" ", parts));
}
// Otherwise keep the line how it is.
else
result.Add(line);
i++;
}
// Returns the new list so it can be formatted further.
return result;
}
static List<string> concatRequirement(List<string> list)
{
// Creates a new list to return with new format.
var result = new List<string>();
// Checks each line in the list.
foreach (var line in list)
{
// Splits each line into 'parts'
var parts = line.Split(' ');
int test;
// Concats everything together
if (int.TryParse(parts[0], out test) || parts[0].Equals("B"))
result.Add(line);
// Otherwise result - 1
else
result[result.Count - 1] += line;
}
// Returns the new list so it can be formatted further.
return result;
}
static List<string> removeTRequirement(List<string> list)
{
// Creates a new list to return with new format.
var result = new List<string>();
// Checks each line in the list.
foreach (var line in list)
{
// Splits each line into 'parts'
var parts = line.Split(' ');
// Checks if the second 'part' array is "T", if it is, remove "T"
if (parts[1].Equals("T"))
parts[1] = string.Empty;
// Add the new string to the result.
result.Add(string.Join(" ", parts).Replace(" ", " "));
}
// Returns the new list so it can be formatted further.
return result;
}
static List<string> fourDigitRequirement(List<string> list)
{
// Creates a new list to return with new format.
var result = new List<string>();
// Checks each line in the list.
foreach (var line in list)
{
// Splits each line into 'parts'
var parts = line.Split(' ');
int test;
// Checks if the array[0] (digits) is the proper length.
if (int.TryParse(parts[0], out test))
{
// If it is not a length of 4 digits, add "O" to the front until it is.
parts[0] = parts[0].PadLeft(4, '0');
// Add the new string to the result list.
result.Add(string.Join(" ", parts));
}
// Otherwise keep the line how it is.
else
result.Add(line);
}
// Returns the new list so it can be formatted further.
return result;
}
QUESTION:
- How do I get that space in there?
In concatRequirement, you can change:
result[result.Count - 1] += line;
to
result[result.Count - 1] += " " + line;
Without desk-checking the whole thing, I notice that the problem entries are where the value is at the end of the line.
Try appending a space to the line before you split it. Maybe.