I have a DataTable that has the following structure:
Root | Level 1 | Level 2 | Level 3 | Tree L | Tree R
Food 1 18
Fruit 2 11
Red 3 6
Cherry 4 5
Yellow 7 10
Banana 8 9
Meat 12 17
Beef 13 14
Pork 15 16
Using C#, I need to traverse this structure and calculate the correct Tree L and Tree R values for each node. This is just an example, the real structure has several hundred nodes that go out to at least Level 7, but possibly more.
Can anyone suggest how I might approach the code for calculating the left and right values?
So, you want to keep track of the visit order of the tree, but you have the order split into L and R. Those correspond to the "entrance" and "exit" of the Visit function for the Node structure. Assuming a Node class with a Node.Visit() method, you could:
private static List<Tuple<char,Node>> VisitOrder = new List<Tuple<char,Node>>();
public void Visit()
{
VisitOrder.Add(Tuple.Create('L', this));
// whatever visit logic you use here
VisitOrder.Add(Tuple.Create('R', this));
}
When you finish, all you have to do is do is look at the VisitOrder values. They're stored in the List in increasing order, where the index corresponds to it's position in the visit sequence. Each item in the List, then, is a Tuple describing which value it corresponds to and which Node it visited.
Edit:
To get the final output format, you could then do something like:
var LTree = VisitOrder
.Where(t => t.First == 'L')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
var RTree = VisitOrder
.Where(t => t.First == 'R')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
Here is how I figured it out:
private class FolderRow
{
public int Indent
{
get { return this.Row.GetValue("Indent", 0); }
set { this.Row["Indent"] = value; }
}
public int Left
{
get { return this.Row.GetValue("Tree L", 0); }
set { this.Row["Tree L"] = value; }
}
public int Right
{
get { return this.Row.GetValue("Tree R", 0); }
set { this.Row["Tree R"] = value; }
}
public Guid Id
{
get { return this.Row.GetValue("FolderID", Guid.Empty); }
}
public DataRow Row { get; private set; }
public int RowNum { get; set; }
public bool RowComplete { get { return this.Left > 0 && this.Right > 0; } }
public FolderRow(DataRow row, int rowNum)
{
this.Row = row;
this.RowNum = rowNum;
}
}
[TestMethod]
public void ImportFolderStructure()
{
var inputTable = FileUtil.GetDataSetFromExcelFile(new FileInfo(#"c:\SampleTree.xlsx")).Tables[0];
var currentLevel = 0;
var nodeCount = 1;
var currentRow = 0;
inputTable.Columns.Add("Indent", typeof (int));
inputTable.Columns.Add("Path", typeof (string));
var rightStack = new List<FolderRow>();
foreach (DataRow row in inputTable.Rows)
row["Indent"] = GetLevelPopulated(row);
foreach (DataRow row in inputTable.Rows)
{
if (row.GetValue("Indent", 0) == 0)
row["Tree L"] = 1;
}
while (true)
{
var row = GetRow(inputTable, currentRow);
if (row.Indent == 0)
{
currentRow++;
rightStack.Add(row);
continue;
}
// if the indent of this row is greater than the previous row ...
if (row.Indent > currentLevel)
{
currentLevel++;
nodeCount++;
row.Left = nodeCount;
// ... check the next row to see if it is further indented
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent == currentLevel)
{
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent < currentLevel)
{
currentLevel--;
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
}
if (inputTable.Rows.Cast<DataRow>().Select(r => new FolderRow(r, -1)).All(r => r.RowComplete))
break;
}
}
private int HandleNextRow(FolderRow row, int currentRow, List<FolderRow> rightStack, DataTable inputTable, ref int currentLevel,
ref int nodeCount)
{
var nextRow = GetRow(inputTable, currentRow + 1);
if (nextRow != null)
{
if (nextRow.Indent > row.Indent)
{
// ok the current row has a child so we will need to set the tree right of that, add to stack
rightStack.Add(row);
}
else if (nextRow.Indent == row.Indent)
{
nodeCount++;
row.Right = nodeCount;
}
else if (nextRow.Indent < row.Indent)
{
nodeCount++;
row.Right = nodeCount;
nodeCount++;
// here we need to get the most recently added row to rightStack, set the Right to the current nodeCount
var stackLast = rightStack.LastOrDefault();
if (stackLast != null)
{
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
currentLevel--;
}
currentRow++;
if (rightStack.Count > 1)
{
// before we move on to the next row, we need to check if there is more than one row still in right stack and
// the new current row's ident is less than the current branch (not current leaf)
var newCurrentRow = GetRow(inputTable, currentRow);
var stackLast = rightStack.LastOrDefault();
if (newCurrentRow.Indent == stackLast.Indent)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
}
}
else
{
// reached the bottom
nodeCount++;
row.Right = nodeCount;
// loop through the remaining items in rightStack and set their right values
var stackLast = rightStack.LastOrDefault();
while (stackLast != null)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
stackLast = rightStack.LastOrDefault();
}
}
return currentRow;
}
private FolderRow GetRow(DataTable inputTable, int rowCount)
{
if (rowCount >= inputTable.Rows.Count)
return null;
return rowCount < 0 ? null : new FolderRow(inputTable.Rows[rowCount],rowCount);
}
private int GetLevelPopulated(DataRow row)
{
var level = 0;
while (level < 14)
{
if (level == 0 && row["Root"] != DBNull.Value)
return level;
level++;
if (row["Level " + level] != DBNull.Value)
return level;
}
return -1;
}
Related
In a bidirectional graph check if a path exists between node A and node B.
My code does not work for certain input (samples provided below). Is this implementation correct or I missed something?
bool[] visited = null;
public bool ValidPath(int n, int[][] edges, int start, int end) {
IList<int>[] adjacencyList = new List<int>[n];
visited = new bool[n];
// create adjacency list
for(int i = 0; i < edges.Length; i++){
int a = edges[i][0];
int b = edges[i][1];
if(adjacencyList[a] == null)
adjacencyList[a] = new List<int>();
adjacencyList[a].Add(b);
if(adjacencyList[b] == null)
adjacencyList[b] = new List<int>();
adjacencyList[b].Add(a);
}
return DFS(adjacencyList, start, end);
}
private bool DFS(IList<int>[] adjacencyList, int start, int end){
if(start == end) return true;
if(!visited[start]){
visited[start] = true;
foreach(var v in adjacencyList[start]){
if(v == end) return true;
if(!visited[v])
DFS(adjacencyList, v, end);
}
}
return false;
}
This works for below examples:
Input: n = 3, edges = [[0,1],[1,2],[2,0]], start = 0, end = 2
Output: true
Input: n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], start = 0, end = 5
Output: false
Does not work for below (expected return value is true but I get false):
Input: 10, edges = [[4,3],[1,4],[4,8],[1,7],[6,4],[4,2],[7,4],[4,0],[0,9],[5,4]], start = 5, end = 9
The approach is correct, with the only issue being that inside the recursive DFS, you need to keep track of the result of all the subsequent DFS calls. If any of the recursive calls return true, then the solution should return true (instead of directly returning false). Here is the slightly modified version of the DFS function that works :
private bool DFS(IList<int>[] adjacencyList, int start, int end){
if(start == end) return true;
bool status = false;
if(!visited[start]){
visited[start] = true;
foreach(var v in adjacencyList[start]){
if(v == end) {
return true;
}
if(!visited[v]) {
status = status || DFS(adjacencyList, v, end);
}
}
}
return status;
}
Update :
Thanks to the suggestion in the comments. If DFS for any of the adjacent vertices returns true, then the method can directly return true (instead of making DFS call for other adjacent vertices and keeping that status). That gives us another variation of DFS :
private bool DFS(IList<int>[] adjacencyList, int start, int end){
if (start == end) return true;
bool status = false;
if(!visited[start]){
visited[start] = true;
foreach(var v in adjacencyList[start]){
if(v == end) {
return true;
}
if(!visited[v] && DFS(adjacencyList, v, end)) {
return true;
}
}
}
return status;
}
Check this, it will pass all the testcases. It is based on BFS.
//int[][] edges = new int[][]
//{
// new int[] { 0, 1 },
// new int[] { 1, 2 },
// new int[] { 2, 0 }
//};
public static bool ValidPath(int[][] edges, int source, int destination)
{
if (source == destination) return true;
var graph = new Dictionary<int, List<int>>();
for (int i = 0; i < edges.Length; i++)
{
var pair = edges[i];
if (!graph.ContainsKey(pair[0])) graph.Add(pair[0], new List<int>());
if (!graph.ContainsKey(pair[1])) graph.Add(pair[1], new List<int>());
graph[pair[0]].Add(pair[1]);
graph[pair[1]].Add(pair[0]);
}
var visited = new HashSet<int>();
visited.Add(source);
var queue = new Queue<int>();
queue.Enqueue(source);
while (queue.Count > 0)
{
var vertex = queue.Dequeue();
if (vertex == destination) return true;
foreach (var next in graph[vertex])
{
if (visited.Contains(next)) continue;
visited.Add(next);
queue.Enqueue(next);
}
}
return false;
}
in a rectangular array, I need to obtain a path consists of a series of ones starting from a given starting point (cell) in which to be the longest. I made a simple search of what are the suitable search algorithms to implement such a task and found that the prime algorithm is the most preferable technique.
Is it actually "Prime Algorithm" or other?
I tried using standard recursive based on Dijkstra's algorithm but I could not get the correct path.
The following code is for the windows form application, some times it output a correct results and sometimes not:
namespace AUV_Topology
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
/* Class Cell */
/*****************************************************************************************************************************/
public class Cell
{
public int row { get; set; }
public int col { get; set; }
public int value { get; set; }
public Boolean visited { get; set; }
}
/* Class EnumCell */
/*****************************************************************************************************************************/
public class EnumCell : IEnumerator<Cell>
{
public EnumCell() { }
public EnumCell(int startRow, int startCol, List<List<Cell>> graph)
{
this.row = startRow;
this.col = startCol;
this.numberOfRows = graph.Count;
this.numberOfCols = graph[0].Count;
this.graph = graph;
}
public enum XY
{
Y = 0, //row
X = 1 //col
}
public enum LOCATIONS : byte
{
TOP_LEFT = 0,
TOP_MIDDLE,
TOP_RIGHT,
LEFT,
RIGHT,
BOTTOM_LEFT,
BOTTOM_MIDDLE,
BOTTOM_RIGHT,
END,
INVALID
}
public List<List<Cell>> graph { get; set; }
public int row { get; set; }
public int col { get; set; }
public int numberOfRows { get; set; }
public int numberOfCols { get; set; }
//offsets are in same order as enum location as y-offset(row), x-offset (col)
private List<List<int>> offsets = new List<List<int>>() {
new List<int>() { -1, -1 },
new List<int>() { -1, 0 },
new List<int>() { -1, +1 },
new List<int>() { 0, -1 },
new List<int>() { 0, +1 },
new List<int>() { +1, -1 },
new List<int>() { +1, 0 },
new List<int>() { +1, +1 }
};
public LOCATIONS position { get; set; }
public EnumCell GetEnumerator()
{
return new EnumCell(row, col, graph);
}
object IEnumerator.Current
{
get {
return Current;
}
}
/* Class Current Cell */
/*****************************************************************************************************************************/
public Cell Current
{
get {
try {
// move to first valie postion
for (LOCATIONS location = position; location < LOCATIONS.END; location++) {
if ((row + offsets[(byte)location][(int)XY.Y] >= 0) && (row + offsets[(byte)location][(int)XY.Y] < numberOfRows) &&
(col + offsets[(byte)location][(int)XY.X] > 0) && (col + offsets[(byte)location][(int)XY.X] < numberOfCols)) {
position = (LOCATIONS)location;
int newRow = row + offsets[(byte)location][(int)XY.Y];
int newCol = col + offsets[(byte)location][(int)XY.X];
return graph[newRow][newCol];
}
}
throw new InvalidOperationException();
} catch (IndexOutOfRangeException) {
throw new InvalidOperationException();
}
}
}
public Boolean MoveNext()
{
Boolean results = false;
for (LOCATIONS location = ++position; location < LOCATIONS.END; location++) {
int y = offsets[(byte)location][(int)XY.Y];
int x = offsets[(byte)location][(int)XY.X];
if ((row + y >= 0) && (row + y < numberOfRows) &&
(col + x > 0) && (col + x < numberOfCols)) {
if (graph[row + y][col + x].value == 1) {
position = (LOCATIONS)location;
return true;
}
}
}
return results;
}
public void Reset()
{
position = LOCATIONS.TOP_LEFT;
}
public void Dispose()
{
}
}
/* Class Graph */
/*****************************************************************************************************************************/
public class Graph
{
public Graph(int[,] graph)
{
this.graph = new List<List<Cell>>();
for (int row = 0; row < graph.GetLength(0); row++) {
List<Cell> newRow = new List<Cell>();
this.graph.Add(newRow);
for (int col = 0; col < graph.GetLength(1); col++) {
Cell newCell = new Cell();
newRow.Add(newCell);
newCell.row = row;
newCell.col = col;
newCell.value = graph[row, col];
newCell.visited = false;
}
}
}
public List<List<Cell>> graph;
}
/* Class SpanningTree */
/*****************************************************************************************************************************/
class SpanningTree
{
public static Graph graph = null;
public static SpanningTree root = new SpanningTree();
public static int counter = 0;
public int row { get; set; }
public int col { get; set; }
public int length { get; set; }
public List<SpanningTree> children { get; set; }
public SpanningTree() { }
public SpanningTree(int startRow, int startCol, int[,] graph)
{
SpanningTree.graph = new Graph(graph);
RecursiveTree(root, SpanningTree.graph.graph[startRow][startCol]);
}
public int RecursiveTree(SpanningTree parent, Cell currentCell)
{
int length = 0;
int maxLength = 0;
parent.row = currentCell.row;
parent.col = currentCell.col;
graph.graph[currentCell.row][currentCell.col].visited = true;
EnumCell enumCell = new EnumCell(currentCell.row, currentCell.col, graph.graph);
foreach (Cell cell in enumCell) {
if (!cell.visited) {
SpanningTree newBranch = new SpanningTree();
if (parent.children == null) parent.children = new List<SpanningTree>();
parent.children.Add(newBranch);
length = RecursiveTree(newBranch, SpanningTree.graph.graph[cell.row][cell.col]);
if (length > maxLength) maxLength = length;
}
}
graph.graph[currentCell.row][currentCell.col].visited = false;
parent.length = maxLength;
return maxLength + 1;
}
public static void OrderHighLow(SpanningTree parent, int level)
{
if (parent.children != null) {
parent.children = parent.children.OrderByDescending(x => x.length).ToList();
foreach (SpanningTree child in parent.children) {
OrderHighLow(child, level + 1);
}
}
}
public static void Print(SpanningTree parent, int level, int chromosomeNum)
{
FileStream fs = new FileStream("C:/Users/Welcome/Desktop/TreeParser.txt", FileMode.Append, FileAccess.Write);
using (StreamWriter sw = new StreamWriter(fs)) {
sw.WriteLine("------------------- Chromosome : {0} -------------------", chromosomeNum);
Print(parent, level, sw);
sw.WriteLine("---------Longest----------");
PrintLongest(parent, level, sw);
counter = 0;
}
}
private static void Print(SpanningTree parent, int level, StreamWriter sw)
{
//////////////////////////////////////////////////////////////////////
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
//////////////////////////////////////////////////////////////////////
if (parent.children != null) {
foreach (SpanningTree child in parent.children) {
Print(child, level + 1, sw);
if (child.length == 0) {
sw.WriteLine("||,,,,,,Branch {0},,,,,,||", counter);
level = 0;
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, root.row, root.col, root.length);
counter += 1;
}
}
}
}
public static void PrintLongest(SpanningTree parent, int level, StreamWriter sw)
{
//////////////////////////////////////////////////////////////////////
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
//////////////////////////////////////////////////////////////////////
if (parent.children != null) {
PrintLongest(parent.children[0], level + 1, sw);
}
}
}
}// end name space
I made call in the main form:
int tt = 0;
foreach (int[,] graph in rectArrays)
{
new SpanningTree(indexesX[tt], indexesY[tt], graph);
SpanningTree.OrderHighLow(SpanningTree.root, 0);
SpanningTree.Print(SpanningTree.root, 0,tt);
tt++;
}
Here is the latest results : Debug
Problem 1
Sometimes the path not as long as expected i.e. some cases (cells) not included while it should be!, sometimes the path contains cells with value zero, and sometimes it move from a cell to another cell while they are not adjacent to each other!.
Problem 2
When I use a big matrix such as 9 x 9 or above; I got the following:
Exception of type 'System.OutOfMemoryException' was thrown.
In method RecursiveTree replace the condition
if (!cell.visited)
by
if (!cell.visited && cell.value == 1)
This restricts the search to cells with value = 1 as required.
Your algorithm is rather complicated and entangled. Also, the naming makes it difficult to guess what things do. For instance, it is not clear what EnumCell enumerates. The name suggests that it is a kind of universal enumerator; however, MoveNext() returns only positions with value == 1. Why is there a variable results which is always false? Why is the logic split between MoveNext() and Current? Current should only return the value found in MoveNext. If MoveNext works correctly, no other logic should be required in Current. There are a lot of other such oddities in your code. C# has a yield return statement which makes writing an enumerator much easier.
Try to simplify your code and debug it.
I implemented a backtracking solution that returns all solutions with the maximum path length. Except for setup and printing the result, there is no other logic involved:
Declarations:
private int[,] _world;
private bool[,] _visited;
[DebuggerDisplay("Coord({Row}, {Col})")]
private struct Coord
{
public Coord(int row, int col)
{
this.Row = row;
this.Col = col;
}
public readonly int Row;
public readonly int Col;
}
Backtracking algorithm (call Find on a valid starting point of a path):
// Returns a list of paths of maximum length consisting of lists of coordinates.
private List<List<Coord>> Find(int row, int col)
{
_visited[row, col] = true;
var allResults = new List<List<Coord>>();
for (int i = Math.Max(0, row-1); i <= Math.Min(_world.GetLength(0)-1, row+1); i++) {
for (int j = Math.Max(0, col-1); j <= Math.Min(_world.GetLength(1)-1, col+1); j++) {
if (!_visited[i, j] && _world[i, j] == 1) {
List<List<Coord>> result = Find(i, j);
allResults.AddRange(result);
}
}
}
if (allResults.Count == 0) {
// This is an end-point of a path. Create the new path with current coord.
// We construct the paths backward.
allResults.Add(new List<Coord> { new Coord(row, col) });
} else {
// Keep only longest results
int maxLength = allResults.Max(p => p.Count);
for (int i = allResults.Count - 1; i >= 0; i--) {
if (allResults[i].Count < maxLength) {
allResults.RemoveAt(i);
} else {
// Prepend current point to path.
allResults[i].Insert(0, new Coord(row, col));
}
}
}
_visited[row, col] = false;
return allResults;
}
Note that the full result tree exists only on the call stack, but not as an explicit data structure. The paths (coordinate lists) are created from the path end-points backwards to the starting point.
With this world
private int[,] _world = new[,] {
{ 0, 1, 0, 1 },
{ 0, 1, 0, 1 },
{ 0, 1, 1, 0 },
{ 1, 0, 0, 1 },
{ 1, 1, 0, 1 },
};
For the starting coordinates (row = 0, column = 1) my solution prints (the printing routine itself is not shown):
Result #1, Length = 7
| ||1|| ||·|
| ||2|| ||·|
| ||4||3|| |
|5|| || ||·|
|6||7|| ||·|
Result #2, Length = 7
| ||1|| ||·|
| ||2|| ||·|
| ||4||3|| |
|5|| || ||·|
|7||6|| ||·|
If you comment out the section discarding shorter results, you can see that there are 8 possible paths (2 of length 5, 4 of length 6 and 2 of length 7).
I have a very unique situation in which i am generating a file for which i have to use nested loops.
So for the current file i have 4 level of nested foreach (). when the data is less its performance is ok ok type still not good but when data start growing the loop grows exponentially due to nested.
Hence it is taking lot of time. Please suggest me some alternative or how can i optimize my code.
Use case:
The file i am trying to print say is having a blue print of the structure which have these nested loop, so i had to go with nesting .
Eg:
Member Details (Level 1)
Health Coverage (Level 2)
Provider Information (Level 3)
BeneFits (Level 4)
So Member details which can have multiple Health Coverage in which each health Coverage can have multiple Provider in which each Provider can have multiple benefits.
hope this help with my situation in a real time example
Level1
foreach()
{
//do some stuff
//writer.writeline();
level2
foreach()
{
//do some stuff
//writer.writeline();
level3
foreach()
{
//do some stuff
//writer.writeline();
level4
foreach()
{
//do some stuff
//writer.writeline();
}
}
}
}
Code
In the method used below writeWholeLine() , it again contain 3 nested for each loop, was not able to post the code here due to limit of character of body
private string TransactionsGeneration(StreamWriter writer, string line, int maximumCount)
{
#region Re-Generation
TransactionCounter = 0;
foreach (DataRow memRow in MemberTblMaster.Rows)
{
TransactionCounter++;
line = string.Empty; //begin of a new Transaction
//Counter
TotalLines = 0;
ST_SE_UniqueCode = 0;
// Fill the dataset based on the member id
MemberID = Convert.ToString(memRow[MEMBER_ID]).Trim();
HealthCoverageTbl = HealthCoverageTblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
Associations834Tbl = Associations834TblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
AddressTbl = AddressTblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
GenNameInfoTbl = GenNameInfoTblMaster.AsEnumerable().Where(x => x.Field<string>(Gen_Name_ID).Trim() == memRow[Sponsor_ID].ToString().Trim() ||
x.Field<string>(Gen_Name_ID).Trim() == memRow[Payer_ID].ToString().Trim() ||
x.Field<string>(Gen_Name_ID).Trim() == memRow[TPA_Broker_ID].ToString().Trim()
);
ContactTbl = ContactTblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
GenReferenceTbl = GenReferenceTblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
MemberTbl = MemberTblMaster.AsEnumerable().Where(x => x.Field<string>(MEMBER_ID).Trim() == MemberID);
// Based on Health Coverage
//Provider , COB
var loopLevel1 = (from row in LoopOrder.AsEnumerable()
where row.Field<int>(HIERARCHY_LEVEL) == 1
&& !Header.Contains(row.Field<string>(LOOP_ID).Trim())
&& !Footer.Contains(row.Field<string>(LOOP_ID).Trim())
select row);
foreach (DataRow parentLoop in loopLevel1)
{
//Level 1
//TODO : Need to implement the parent loop functionality
ParentLoop = Convert.ToString(parentLoop[PARENT_LOOP]);
string loopIDofLoopOrder = parentLoop[LOOP_ID].ToString();
LoopID = loopIDofLoopOrder;
var resultLevel1 = (from row in ValidationElementAttribute.AsEnumerable()
where row.Field<string>(LoopIdSegment).Trim() == loopIDofLoopOrder
select row);
if (resultLevel1.Any())
{
int maxCount1;
if (String.IsNullOrEmpty(Convert.ToString(parentLoop[Repeat_max])))
maxCount1 = maximumCount; //Max_Repitition = NULL means infinite number of repititions allowed; no upper cap
else
maxCount1 = Convert.ToInt32(parentLoop[Repeat_max]);
for (int i = 0; i < maxCount1; i++) //until all the repititions are covered, keep repeating the same loop, else change the parent loop
{
SkipLine = false;
WriteWholeLine(line, i, resultLevel1, writer, memRow);
#region Level 2
var loopLevel2 = (from row in LoopOrder.AsEnumerable()
where row.Field<int>(HIERARCHY_LEVEL) == 2
&& row.Field<string>(PARENT_LOOP).Trim() == loopIDofLoopOrder.Trim()
select row);
foreach (DataRow level2 in loopLevel2)
{
//Level 2
// ChildLoop = Convert.ToString(level2["PARENT_LOOP"]);// 1000C
ChildLoop = Convert.ToString(level2[LOOP_ID]);// 1100C
LoopID = ChildLoop;
var resultLevel2 = (from row in ValidationElementAttribute.AsEnumerable()
where row.Field<string>(LoopIdSegment).Trim() == ChildLoop.Trim()
select row);
//var healthCoverageIdList = memberEnrollment.Select(x => x.Field<object>(Health_Coverage_ID)).Distinct().ToList();
if (resultLevel2.Any())
{
int maxCount2;
if (String.IsNullOrEmpty(Convert.ToString(level2[Repeat_max])))
maxCount2 = maximumCount;
else
maxCount2 = Convert.ToInt32(level2[Repeat_max]);
//Custom Code
// maxCount2= ChildLoop == _2300 ? healthCoverageIdList.Count : maxCount2;
for (int j = 0; j < maxCount2; j++)
{
SkipLine = false;
//Custom Code
//if (ChildLoop == "2300")
//{
// WriteWholeLine(line, j, resultLevel2, writer, memRow, memberEnrollment.Where(x => x.Field<object>(Health_Coverage_ID) == healthCoverageIdList[j]).Select(x => x));
//}
//else
//{
//WriteWholeLine(line, j, resultLevel2, writer, memRow, memberEnrollment);
//}
WriteWholeLine(line, j, resultLevel2, writer, memRow);
if (HealthCoverageTbl.Any() && HealthCoverageTbl.Count() > j)
{
HealthCoverageID = Convert.ToString(HealthCoverageTbl.ElementAt(j).Field<string>(Health_Coverage_ID)).Trim();
}
else
{
HealthCoverageID = string.Empty;
}
#region Level 3
var loopLevel3 = (from row in LoopOrder.AsEnumerable()
where row.Field<int>(HIERARCHY_LEVEL) == 3
&& row.Field<string>(PARENT_LOOP).Trim() == ChildLoop.Trim()
select row);
foreach (DataRow level3 in loopLevel3)
{
//Level 3
ChildLoopLevel3 = Convert.ToString(level3[LOOP_ID]);
LoopID = ChildLoopLevel3;
var resultLevel3 = (from row in ValidationElementAttribute.AsEnumerable()
where row.Field<string>(LoopIdSegment).Trim() == ChildLoopLevel3.Trim()
select row);
if (resultLevel3.Any())
{
CobInfoTbl = CobInfoTblMaster.AsEnumerable().Where(x => x.Field<string>(Health_Coverage_ID).Trim() == HealthCoverageID).Select(x => x);
ProviderTbl = ProviderTblMaster.AsEnumerable().Where(x => x.Field<string>(Health_Coverage_ID).Trim() == HealthCoverageID).Select(x => x);
LXcounter = 0;
int maxCount3;
if (String.IsNullOrEmpty(Convert.ToString(level3[Repeat_max])))
maxCount3 = maximumCount;
else
maxCount3 = Convert.ToInt32(level3[Repeat_max]);
for (int k = 0; k < maxCount3; k++)
{
SkipLine = false;
if (CobInfoTbl.Any() && CobInfoTbl.Count() > k)
{
CobInfoID = CobInfoTbl.ElementAt(k).Field<string>(COB_ID);
}
else
{
CobInfoID = Convert.ToString("0");
}
//Not used : uncomment if Provider ID needed.
if (ProviderTbl.Any() && ProviderTbl.Count() > k)
{
ProviderID = ProviderTbl.ElementAt(k).Field<string>(Provider_ID).Trim();
}
else
{
ProviderID = string.Empty;
}
WriteWholeLine(line, k, resultLevel3, writer, memRow);
#region Level 4
var loopLevel4 = (from row in LoopOrder.AsEnumerable()
where row.Field<int>(HIERARCHY_LEVEL) == 4
&& row.Field<string>(PARENT_LOOP).Trim() == ChildLoopLevel3.Trim()
select row);
foreach (DataRow level4 in loopLevel4)
{
//Level 4
ChildLoopLevel4 = Convert.ToString(level4[LOOP_ID]);
LoopID = ChildLoopLevel4;
var resultLevel4 = (from row in ValidationElementAttribute.AsEnumerable()
where row.Field<string>(LoopIdSegment).Trim() == ChildLoopLevel4.Trim()
select row);
if (resultLevel4.Any())
{
int maxCount4;
if (String.IsNullOrEmpty(Convert.ToString(level4[Repeat_max])))
maxCount4 = maximumCount;
else
maxCount4 = Convert.ToInt32(level4[Repeat_max]);
for (int l = 0; l < maxCount4; l++)
{
SkipLine = false;
WriteWholeLine(line, l, resultLevel4, writer, memRow);
}
}
}
#endregion
}
}
}
#endregion
}
}
}
#endregion
}
}
}
// TODO : remove below break
// break;
}
//end of Regeneration
#endregion
return line;
}
if the order of the entries does not matter as in the results of each nested loop dont need to have the structure the foreach source has.
A->B->C->D->E each of those represent a nested loop, you could do it in parallel.
since all data manipulation should be the same, rewrite all foreach as in parallel. save the results to a collection like a ConcurrentDictionary
It's fairly straight-forward to add a TextField using ABCPDF:
public FormField AddTextField(string inRect, string inName, string inText)
{
int fieldId = mDoc.AddObject("<</Type /Annot /Subtype /Widget /F 4 /FT /Tx /Ff 4096 /Q 1>>");
mDoc.SetInfo(fieldId, "/V:Text", inText);
RegisterField(fieldId, inName, inRect);
return new FormField(fieldId, mDoc);
}
With this as the implementation:
FormField text = AddTextField("40 530 300 580", "TextField1", "Hello World!");
text.DefaultAppearance = "/TimesRoman 36 Tf 0 0 1 rg";
text.BorderColor = "0 0 0";
text.FillColor = "220 220 220";
text.TextAlign = "Left";
If I need to add two fields that have the same name, it's a little more complex:
public int AddGroupField(FormField[] inKids, string inName, string inValue)
{
if (inKids.Length == 0)
throw new Exception("Cannot have a group field with no kids");
string ft = null, dv = null;
int fieldId = mDoc.AddObject("<< /Kids [] >>");
foreach (FormField kid in inKids)
{
mDoc.SetInfo(fieldId, "/Kids[]:Ref", kid.Id.ToString());
mDoc.SetInfo(kid.Id, "/Parent:Ref", fieldId);
if (ft == null)
ft = mDoc.GetInfo(kid.Id, "/FT");
if (dv == null)
dv = mDoc.GetInfo(kid.Id, "/DV");
mDoc.SetInfo(kid.Id, "/FT:Del", "");
mDoc.SetInfo(kid.Id, "/V:Del", "");
mDoc.SetInfo(kid.Id, "/DV:Del", "");
}
mDoc.SetInfo(fieldId, "/FT", ft);
mDoc.SetInfo(fieldId, "/T:Text", inName);
mDoc.SetInfo(fieldId, "/V:Text", inValue);
if (dv != null)
mDoc.SetInfo(fieldId, "/DV:Text", dv);
int eid = mDoc.GetInfoInt(mDoc.Root, "/AcroForm:Ref");
mDoc.SetInfo(eid, "/Fields*[]:Ref", fieldId);
return fieldId;
}
With this as the implementation:
FormField[] kids = new FormField[2];
kids[0] = AddTextField("40 230 300 280", null, null);
kids[1] = AddTextField("40 170 300 220", null, null);
int id = AddGroupField(kids, "TextField1", "Hello World!");
However, I'm having issues with adding a TextField to a PDF where a TextField already exists with that same name. So, for example, if my PDF already has a field named "TextField1" and then I want to add another field with that same name, none of the above implementations will work.
I was able to get the answer from ABCPDF support. And as you can see, it's not exactly a simple process. I'm not sure anyone could just figure this out on their own without spending months and months of research on the PDF spec and the ABCPDF product.
public FormField AddTextField(string inRect, string inName, string inText)
{
bool fieldAlreadyExists = mDoc.Form[inName] != null;
int fieldId = mDoc.AddObject("<</Type /Annot /Subtype /Widget /F 4 /FT /Tx /Ff 4096 /Q 1>>");
mDoc.SetInfo(fieldId, "/V:Text", inText);
RegisterField(fieldId, inName, inRect);
var field = new FormField(fieldId, mDoc);
if (fieldAlreadyExists)
{
InteractiveForm form = new InteractiveForm(mDoc);
form.AddFieldIntoExistingGroup(field, true);
}
return field;
}
private bool AddFieldIntoExistingGroup(FormField field, bool refreshForm)
{
bool duplicatesFound = false;
int acroFormID = mDoc.GetInfoInt(mDoc.Root, "/AcroForm:Ref");
int parentID = mDoc.GetInfoInt(field.Id, "/Parent:Ref");
ArrayAtom kids;
if (parentID > 0)
{
kids = mDoc.ObjectSoup.Catalog.Resolve(Atom.GetItem(mDoc.ObjectSoup[parentID].Atom, "Kids")) as ArrayAtom;
}
else
{
kids = mDoc.ObjectSoup.Catalog.Resolve(Atom.GetItem(mDoc.ObjectSoup[acroFormID].Atom, "Fields")) as ArrayAtom;
}
Dictionary<string, List<IndirectObject>> items = new Dictionary<string, List<IndirectObject>>();
for (int i = 0; i < kids.Count; i++)
{
IndirectObject io = mDoc.ObjectSoup.Catalog.ResolveObj(kids[i]);
if (io == null)
{
continue; // shouldn't really happen
}
string name = mDoc.GetInfo(io.ID, "/T:Text");
if (!items.ContainsKey(name))
{
items[name] = new List<IndirectObject>();
}
items[name].Add(io);
}
foreach (KeyValuePair<string, List<IndirectObject>> pair in items)
{
if (pair.Value.Count > 1)
{
duplicatesFound = true;
// shift field down to be a child of a new field node
int id = mDoc.AddObject("<< >>");
if (parentID > 0)
{
mDoc.SetInfo(parentID, "/Kids[]:Ref", id);
mDoc.SetInfo(id, "/Parent:Ref", parentID);
}
else
{
mDoc.SetInfo(acroFormID, "/Fields[]:Ref", id);
}
string[] dictEntries = new[] { "/FT", "/T", "/TU", "/Ff", "/V", "/DV" };
foreach (IndirectObject io in pair.Value)
{
foreach (string dictEntry in dictEntries)
{
string val = mDoc.GetInfo(io.ID, dictEntry);
if (!string.IsNullOrEmpty(val))
{
mDoc.SetInfo(id, dictEntry, val);
mDoc.SetInfo(io.ID, dictEntry + ":Del", "");
}
}
ArrayRemoveOneRefAtom(kids, io.ID);
mDoc.SetInfo(id, "/Kids[]:Ref", io.ID);
mDoc.SetInfo(io.ID, "/Parent:Ref", id);
}
}
}
if ((refreshForm) && (duplicatesFound))
{
mDoc.Form.Refresh();
}
return duplicatesFound;
}
private static bool ArrayRemoveOneRefAtom(ArrayAtom array, int id)
{
if (array != null)
{
for (int i = 0; i < array.Count; i++)
{
RefAtom refAtom = array[i] as RefAtom;
if ((refAtom != null) && (refAtom.ID == id))
{
ArrayRemoveAt(array, i);
return true;
}
}
}
return false;
}
private static void ArrayRemoveAt(ArrayAtom array, int index)
{
if (index == 0)
{ // Workaround for bug in some versions of ABCpdf
Atom[] copy = new Atom[array.Count];
array.CopyTo(copy, 0);
array.Clear();
for (int i = 1; i < copy.Length; i++)
array.Add(copy[i]);
}
else
{
array.RemoveAt(index);
}
}
I have a DataGridView which colors its rows when its States property is set.
States is a String which represents a semicolon-separated numbers list.
If I receive "0;1;2" the three first rows will be colored in purle, green and red respectively.
The problem comes when I sort the datagrid clicking on a column header : colors are applied the same way.
For example :
Names|Labels
Name1|Label1
Name2|Label2
Name3|Label3
I receive "0;1;2" which means "Purple;Green;Red" :
Names|Labels
Name1|Label1 => Purple
Name2|Label2 => Green
Name3|Label3 => Red
I sort (descending):
Names|Labels
Name3|Label3 => Red
Name2|Label2 => Green
Name1|Label1 => Purple
I receive "3;4;5" which means "Yellow;Orange;Pink" :
Names|Labels
Name3|Label3 => Yellow
Name2|Label2 => Orange
Name1|Label1 => Pink
But this isn't what I was waiting for, I wanted that :
Names|Labels
Name3|Label3 => Pink
Name2|Label2 => Orange
Name1|Label1 => Yellow
Here is my code :
protected String m_States;
public virtual String States
{
get { return m_States; }
set {
m_States = value;
if (m_bRunning)
{
UpdateColors();
}
}
}
private void UpdateColors()
{
String[] sStates = new String[] { };
if (m_States != null)
{
sStates = m_States.Split(m_sSeparators);
int nState = 0;
int nRowNumber = 0;
foreach (System.Windows.Forms.DataGridViewRow row in Rows)
{
nState = int.Parse(sStates[nRowNumber]);
if (nState < 0 || nState > m_Couleurs_Fond_Etats.Length)
{
nState = m_Couleurs_Fond_Etats.Length - 1;
}
row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
row.DefaultCellStyle.ForeColor = m_Couleurs_Texte_Etats[nState];
row.DefaultCellStyle.SelectionBackColor = m_Couleurs_Sel_Fond_Etats[nState];
row.DefaultCellStyle.SelectionForeColor = m_Couleurs_Sel_Texte_Etats[nState];
nState = 0;
++nRowNumber;
}
}
}
Isn't there a way to have access to rows the order they were added in the DataGridView?
PS : I first used row.Index instead of nRowNumber, so I believed the problem came from that, but apparently, the Rows collection is reorganized or the foreach parses it according to the rowIndexes.
===== Here is the solution I used thanks to LarsTech's answer =====
After adding my rows, I tagged them this way :
foreach(System.Windows.Forms.DataGridViewRow row in Rows)
{
row.Tag = row.Index;
}
Then I could use this tag as row number :
private void UpdateColors()
{
String[] sStates = new String[] { };
if (m_States != null)
{
sStates = m_States.Split(m_sSeparators);
int nState = 0;
int nRowNumber = 0;
foreach (System.Windows.Forms.DataGridViewRow row in Rows)
{
nRowNumber = Convert.ToInt32(row.Tag);
if (nRowNumber >= 0 && nRowNumber < sEtats.Length)
{
nState = int.Parse(sStates[nRowNumber]);
if (nState < 0 || nState > m_Couleurs_Fond_Etats.Length)
{
nState = m_Couleurs_Fond_Etats.Length - 1;
}
row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
row.DefaultCellStyle.ForeColor = m_Couleurs_Texte_Etats[nState];
row.DefaultCellStyle.SelectionBackColor = m_Couleurs_Sel_Fond_Etats[nState];
row.DefaultCellStyle.SelectionForeColor = m_Couleurs_Sel_Texte_Etats[nState];
nState = 0;
}
}
}
}
You can try using the Tag property of the row to place your row index number. This is how I had my DataGridView initialized:
dataGridView1.Rows.Add(3);
for (int i = 0; i < 3; i++) {
dataGridView1.Rows[i].Tag = i;
dataGridView1.Rows[i].Cells[0].Value = "Name " + i.ToString();
dataGridView1.Rows[i].Cells[1].Value = "Label " + i.ToString();
}
Here is my version of your UpdateColors routine:
private void UpdateColors() {
String[] sStates = new String[] { };
if (m_States != null) {
sStates = m_States.Split(';');
for (int i = 0; i < sStates.Length;i++) {
int nState = Convert.ToInt32(sStates[i]);
foreach (DataGridViewRow row in dataGridView1.Rows) {
int rowIndex = Convert.ToInt32(row.Tag);
if (rowIndex == i) {
row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[nState];
}
}
}
}
}
I am first looping through your splitted state string to get the row index and to convert the actual value to the color index. Then I loop through the rows to find the matching index property that I placed in the Tag property.
Here is a cleaned up version using just the one loop:
private void UpdateColors() {
String[] sStates = new String[] { };
if (m_States != null) {
sStates = m_States.Split(';');
foreach (DataGridViewRow row in dataGridView1.Rows) {
int rowIndex = Convert.ToInt32(row.Tag);
int colorIndex = Convert.ToInt32(sStates[rowIndex]);
row.DefaultCellStyle.BackColor = m_Couleurs_Fond_Etats[colorIndex];
}
}
}
Needs error checking obviously.