Search code examples
c#for-loopms-wordxceed

What is the best way to get cell index of a word table without using for loop in c#?


I am getting the index of the cell of a word table using for loop which takes a lot of time for bigger tables, is there any way to do this without for loop?

public static int[] GetColumnIndex(Xceed.Words.NET.Table table, string columnName, int endRow,int k)
        {
            int[] data = { -1, -1 };
            for (int j = k; j < endRow; j++)
            {
                for (int i = 0; i < table.Rows[j].Cells.Count; ++i)
                {
                    if (table.Rows[j].Cells[i].Paragraphs[0].Text.Equals("«" + columnName + "»"))
                    {
                        data[0] = j;
                        data[1] = i;
                        return data;
                    }
                }
            }

            return data;
        }

and I am calling this function form another function

int startRow = 0, endRow = 0;
int[] ind;
DocX doc;
doc = DocX.Load(fileName);
Xceed.Words.NET.Table t;
t = doc.Tables[0];
endRow = t.Rows.Count;
System.Data.DataTable dt = new DataTable();
dt = reader(report.Query);
foreach (DataColumn col in dt.Columns)
{
    ind = GetColumnIndex(t, col.ColumnName, endRow,2);

    //...more code here...
}


Solution

  • A few things you can do to optimise your algorithm (based on your access pattern) is that you search the same table number of times (in fact, since you are searching column names in the table, number of searches increases quickly as the table gets big). Hence, it would be worth transforming the data in the table to a data structure indexed by the words (for e.g. a Sorted Dictionary).

    Firstly, create a class that holds the content of the table. This way when you want to search the same table, you can use the same instance of the class and avoid recreating the data structure based on the sorted dictionary:

    public class XceedTableAdapter
    {
       private readonly SortedDictionary<string, (int row, int column)> dict;
    
       public XceedTableAdapter(Xceed.Words.NET.Table table)
       {
          this.dict = new SortedDictionary<string, (int, int)>();
          // Copy the content of the table into the dict.
          // If you have duplicate words you need a SortedDictionary<string, List<(int, int)>> type. This is not clear in your question.
          for (var i = 0, i < rowCount; i++)
          {
              for (var j = 0; j < columnCount; j++)
              {
                  // this will overwrite the index if the text was previously found: 
                  this.dict[table.Rows[i].Cells[j].Paragraphs[0].Text] = (i, j);
              }
          }
       }
    
       public (int, int) GetColumnIndex(string searchText)
       {
          if(this.dict.TryGetValue(searchText, out var index))
          {
              return index;
          }
    
          return (-1, -1);
       }
    }
    

    Now you loop the entire table only once and the subsequent searches will happen in O(log n). If Xceed has a function to transform data table to a dictionary, that would be quite handy. I'm not familiar with this library.

    Now you can search it like:

    var searchableTable = new XceedTableAdapter(doc.Tables[0]);
    
    foreach (var col in dt.Columns)
    {
       ind = searchableTable.GetColumnIndex(col);
    }