Find a string in a Range using ClosedXML C# - c#

I want to be able to find if a particular string exists in a range using ClosedXML, however, I cannot find any find command in the documentation. Currently I loop through 1000s of rows to find if the string exists. Is there a more efficient way to do this?
Here is an example of my code:
for (int j = 3; j <= PipeSheet.LastRowUsed().RowNumber(); j ++)
{
if ((PipeSheet.Cell(j, ProdCodeColumnInPipe).Value.ToString().Trim() == SheetToEdit.Cell(i, ProdCodeColumnInMain).Value.ToString().Trim() & PipeSheet.Cell(j, 3).Value.ToString().Trim() == SheetToEdit.Cell(i, RegionCodeInMain).Value.ToString().Trim()))
{
SheetToEdit.Cell(i, ColumnToEdit).Value = "+";
if ((new[] { "Open", "Under Review" }).Contains(PipeSheet.Cell(j, 5).Value.ToString().Trim()) & (new[] { "Forecast"}).Contains(PipeSheet.Cell(j, 4).Value.ToString().Trim()))
{
if (FirstColumnHighlight > 1 & LastColumnHighlight > 1)
{
for (int k = FirstColumnHighlight; k <= LastColumnHighlight; k++)
{
SheetToEdit.Cell(i, k).Style.Fill.BackgroundColor = XLColor.FromArgb(255, 255, 0);
}
}
}
}
}

Firstly, your goal is best solved using conditional formatting.
But to answer your question, you can search for a string:
sheet.CellsUsed(cell => cell.GetString() == searchstring)
Reference: https://github.com/ClosedXML/ClosedXML/wiki/Better-lambdas
-- UPDATE --
There is a pull request at https://github.com/ClosedXML/ClosedXML/pull/399 to help with this, for example:
foundCells = ws.Search("searchText", CompareOptions.OrdinalIgnoreCase);

Related

C# Sorting, return multiple text file string entries line at a time

I have a C# console window program and I am trying to sort "File3" (contains numbers) in ascending and output lines from 3 text files.
So the outcome looks something like this:
===========================================================================
field1.....................field2.....................field3
===========================================================================
[FILE1_LINE1]..............[FILE2_LINE1]..............[FILE3_LINE1]
[FILE1_LINE2]..............[FILE2_LINE2]..............[FILE3_LINE2]
[FILE1_LINE3]..............[FILE2_LINE3]..............[FILE3_LINE3]
and so on...
At the moment, it kinda works I think but it duplicates the first two lines it seems. Could someone give an example of better coding please?
Here is the code that I have atm:
string[] File1 = System.IO.File.ReadAllLines(#"FILE1.txt");
string[] File2 = System.IO.File.ReadAllLines(#"FILE2.txt");
string[] File3 = System.IO.File.ReadAllLines(#"FILE3.txt");
decimal[] File3_1 = new decimal[File3.Length];
for(int i=0; i<File3.Length; i++)
{
File3_1[i] = decimal.Parse(File3[i]);
}
decimal[] File3_2 = new decimal[File3.Length];
for(int i=0; i<File3.Length; i++)
{
File3_2[i] = decimal.Parse(File3[i]);
}
decimal number = 0;
for (double i = 0.00; i < File3_1.Length; i++)
{
for (int sort = 0; sort < File3_1.Length - 1; sort++)
{
if (File3_1[sort] > File3_1[sort + 1])
{
number = File3_1[sort + 1];
File3_1[sort + 1] = File3_1[sort];
File3_1[sort] = number;
}
}
}
if (SortChoice2 == 1)
{
for (int y = 0; y < File3_2.Length; y++)
{
for (int s = 0; s < File3_2.Length; s++)
{
if (File3_1[y] == File3_2[s])
{
Console.WriteLine(File1[s] + File2[s] + File3_1[y]);
}
}
}
}
Just for more info, most of this code was used for another program and worked but in my new program, this doesn't as I've said above - ("it repeats a couple of lines for some reason"). I'm kinda an amateur/ rookie at C# so I only get stuff like this to work with examples.
Thanks in advance :)
Ok, if I understand correctly, what you are trying to do is read the lines from 3 different files, each of them representing a different "field" in a table. You then want to sort this table based on the value of one of the field (in you code, this seems to be the field which values are contained in File3. Well, if I got that right, here's what I suggest you do:
// Read data from files
List<string> inputFileNames = new List<string> {"File1.txt", "File2.txt", "File3.txt"};
decimal[][] fieldValues = new decimal[inputFileNames.Count][];
for (int i = 0; i < inputFileNames.Count; i++)
{
string currentInputfileName = inputFileNames[i];
string[] currentInputFileLines = File.ReadAllLines(currentInputfileName);
fieldValues[i] = new decimal[currentInputFileLines.Length];
for (int j = 0; j < currentInputFileLines.Length; j++)
{
fieldValues[i][j] = decimal.Parse(currentInputFileLines[j]);
}
}
// Create table
DataTable table = new DataTable();
DataColumn field1Column = table.Columns.Add("field1", typeof (decimal));
DataColumn field2Column = table.Columns.Add("field2", typeof (decimal));
DataColumn field3Column = table.Columns.Add("field3", typeof (decimal));
for (int i = 0; i < fieldValues[0].Length; i++)
{
var newTableRow = table.NewRow();
newTableRow[field1Column.ColumnName] = fieldValues[0][i];
newTableRow[field2Column.ColumnName] = fieldValues[1][i];
newTableRow[field3Column.ColumnName] = fieldValues[2][i];
table.Rows.Add(newTableRow);
}
// Sorting
table.DefaultView.Sort = field1Column.ColumnName;
// Output
foreach (DataRow row in table.DefaultView.ToTable().Rows)
{
foreach (var item in row.ItemArray)
{
Console.Write(item + " ");
}
Console.WriteLine();
}
Now, I tried to keep the code above as LINQ free as I could, since you do not seem to be using it in your example, and therefore might not know about it. That being said, while there is a thousand way to do I/O in C#, LINQ would help you a lot in this instance (and in pretty much any other situation really), so I suggest you look it up if you don't know about it already.
Also, the DataTable option I proposed is just to provide a way for you to visualize and organize the data in a more efficient way. That being said, you are in no way obliged to use a DataTable: you could stay with a more direct approach and use more common data structures (such as lists, arrays or even dictionaries if you know what they are) to store the data, depending on your needs. It's just that with a DataTable, you don't, for example, need to do the sorting yourself, or deal with columns indexed only by integers. With time, you'll come to learn about the myriad of useful data structure and native functionalities the C# language offers you and how they can save you doing the work yourself in a lot of cases.

How to add a line feed after each digit that came before a letter

i have a string like that:
String s ="6,44 6,35 +0,63asd 4,27fgh 4,14 45,6 +777,4cvbvc";
I want to insert a line feed into those points between every digit and letter like: +0,63(here must be a line break)asd. I wrote the code below but it gave length changed error. Maybe it may occur either because of my program's features or c#'s feature. But i need to stay stick to the features. So can you recommend me a solution in those limits? The code below could not solve it and i could not find any other way.
int k = s.Length;
for (int i = 0; i < k; i++)
if (char.IsDigit(s[i - 1]) && char.IsLetter(s[i]))
s = s.Insert(i, Strings.Chr(10).ToString());
Thank you for your help. Cheers.
You need to start the cycle from 1 or you will get an exception because when i=0 s[i-1] becomes s[-1].
Now your program should work but i would suggest you to use a stringbuilder because string.Insert creates a new string instance everytime you call it making your program very slow if the input string is long.
Also if you are in a Windows environment a line break is represented by "\r\n" wich is Environment.NewLine in c#.
So your code should be something like this:
StringBuilder builder=new StringBuilder(s);
int offset=0;
for (int i = 1; i < s.Length; i++)
{
if (char.IsDigit(s[i - 1]) && char.IsLetter(s[i]))
{
builder.Insert(i+offset, Environment.NewLine);
//every time you add a line break you need to increment the offset
offset+=Environment.NewLine.Length;
}
}
s=builder.ToString();
please try this code
String myString="";
int k = s.Length;
for (int i = 0; i < k; i++)
if (char.IsDigit(s[i - 1]) && char.IsLetter(s[i]))
myString = myString + Strings.Chr(10).ToString() + s.Substring(i,k-i);
s=myString;
Your solution hase one mistake - loop index range, you should start with 1, because you have expression (i-1)
here is solution
https://dotnetfiddle.net/FMvpqj
You can do like this:
int k = s.Length;
int i= 1;
while (i < s.Length)
{
if ((char.IsDigit(s[i - 1]) && char.IsLetter(s[i])) ||
(char.IsLetter(s[i-1]) && char.IsDigit(s[i])))
{
s = s.Insert(i, ((char)10).ToString());
i++;
}
i++;
}

Summarize table using C# and JScript

I've written code in C# to summarize values of InvoiceTable and move those summarized values to to GroupTable in Abbyy FlexiCapture. The Software is comparatively new and does not show any error when I run it.
There are two sets of code to be written:
In TechField.
In EventHandlers.
InvoiceTable consists of:
TariffNumber
ShipQty
Amount
COO
GroupTable consists of:
HSCode
Qty
Amt
CountryOO
EventHandlers code is as follows (C#):
if (ChangedStates.Has(7)) {
int currentRow = 0;
int i;
for (i = 0; i < Document.Field("Invoice2\\InvoiceTable").Items.Count; i++) {
if (Document.Field("Invoice2\\InvoiceTable").Cell("TariffNumber", i).Value == "") {
Document.Field("Invoice2\\GroupTable").Cell("HSCode", currentRow).Value = Document.Field("Invoice2\\InvoiceTable").Cell("TariffNumber", i).Value;
Document.Field("Invoice2\\GroupTable").Cell("Amt", currentRow).Value = Document.Field("Invoice2\\InvoiceTable").Cell("Amount", i).Value;
Document.Field("Invoice2\\GroupTable").Cell("Qty", currentRow).Value = Document.Field("Invoice2\\InvoiceTable").Cell("ShipQty", i).Value;
currentRow++;
}
}
}
TechField is as follows (JScript):
for (i = 0; i < Field("ShipQty").Items.Count - 1; i++) {
for (j = i + 1; j < Field("ShipQty").Items.Count; j++) {
// if same new items are found
if (Field("TariffNumber").Items(i).Value == Field("TariffNumber").Items(j).Value && Field("CoO").Items(i).Value == Field("CoO").Items(j).Value)
{
// summarise quantities
Field("ShipQty").Items(i).Value = parseInt(Field("ShipQty").Items(i).Value) + parseInt(Field("ShipQty").Items(j).Value);
// and weights
Field("Amount").Items(i).Value = parseFloat(Field("Amount").Items(i).Value) + parseFloat(Field("Amount").Items(j).Value);
}
}
}
Condition:
In the InvoiceTable, where ever TariffNumber and COO are equal, values of ShipQty and Amount should be summarized and put into GroupTable.
The code does not show any errors but does not give the output as well. Would be great if anyone of you can help me out on this.
One thing you could try is adding a summary section to the document definition. This may require you to create a document set.
I've found it easier to create rules which are checked when the field is recognised. There's some info here: https://help.abbyy.com/en-us/flexicapture/12/distributed_administrator/docsets_settings/

how to format in RTF using c#?

I have below text in richTextBox control.
i want to format text like below text
The RTF box can help you here, the only help using RTF will be using a table as Kosala mentioned.
You may use string operations for that:
int equalPos = 20;
for (int l = 0; l < rtfBox.Lines.Length; l++) {
int i = rtfBox.lines[i].IndexOf('=');
int n = equalPos - i;
if ((i >= 0) && (n > 0)) {
rtfBox.lines[i] = rtfBox.lines[i].Insert(i, new string(' ', n));
}
}
Wrote this from head, so please check for errors.
EDIT:
Ok, here's another one:
for (int l = 0; l < rtfBox.Lines.Length; l++) {
int i = rtfBox.lines[i].IndexOf('=');
if (i >= 0) {
rtfBox.lines[i] = rtfBox.lines[i].Insert(i, "\t");
}
}
rtfBox.SelectAll();
rtfBox.SelectionTabs = new int[] { 100 }; // Find a value big enough!
This is what I would do.(These are manual steps.. :)
1).Open MSWord.
2).Create a table; 2 columns and 5 rows (this is for your text)
3). put the text that you wanted to format in to correct cells of the table
4). Save Word Doc as a rtf file.
5). Open rtf file in notepad(notepad++ is better)
There it is.. Now you can find how it is formatted. Now it should not be hard for you to do it in C#. Good luck.

How to write groups of numbers using Console.Write?

I'm very new to C# (And Stack Overflow, forgive me for any poor etiquette here), and I'm writing the game Mastermind in a console application. I'm trying to show a list of the user's guesses at the end of the game, and I know that using Console.WriteLine(); will just give me 30-odd lines off numbers which don't tell the user anything.
How can I alter my code so that the program displays 4 numbers in a group, at a time? For example:
1234
1234
1234
//Store numbers in a history list
ArrayList guesses = new ArrayList(); //This is the ArrayList
Console.WriteLine("Please enter your first guess.");
guess1 = Convert.ToInt32(Console.ReadLine());
guesses.Add(guess1);
foreach (int i in guesses)
{
Console.Write(i);
}
I assume that each element of your byte array is a single digit (0-9). If that assumption is invalid -- please let me know, I'll modify the code :)
Action<IEnumerable<int>> dump = null;
dump = items =>
{
if(items.Any())
{
var head = String.Join("", items.Take(4));
Console.WriteLine(head);
var tail = items.Skip(4);
dump(tail);
}
};
dump(guesses);
It looks like you're most of the way there, you have a console write that writes them all out without linebreaks. Next add an integer count and set it to zero. Increment it by one in the foreach loop. count % 4 == 0 will then be true for all counts that are a multiple of four. This means you can stick an if block there with a write-line to give you your groups of four.
List<int> endResult = new List<int>();
StringBuilder tempSb = new StringBuilder();
for(int i=0; i < groups.Count; i++)
{
if(i % 4 == 0) {
endResult.Add(int.Parse(sb.ToString()));
tempSb.Clear(); // remove what was already added
}
tempSb.Append(group[i]);
}
// check to make sure there aren't any stragglers left in
// the StringBuilder. Would happen if the count of groups is not a multiple of 4
if(groups.Count % 4 != 0) {
groups.Add(int.Parse(sb.ToString()));
}
This will give you a list of 4 digit ints and make sure you don't lose any if your the number of ints in your groups list is not a multiple of 4. Please note that I am continuing based on what you provided, so groups is the ArrayList of ints.
This is some thing I quickly put together:
Update:
ArrayList guesses = new ArrayList(); //This is the ArrayList
// Four or more
guesses.Add(1); guesses.Add(2);
guesses.Add(3);guesses.Add(4);
guesses.Add(5); guesses.Add(6); guesses.Add(7);guesses.Add(8); guesses.Add(9);
//Uncomment-Me for less than four inputs
//guesses.Add(1); guesses.Add(2);
int position = 0;
if (guesses.Count < 4)
{
for (int y = 0; y < guesses.Count; y++)
{
Console.Out.Write(guesses[y]);
}
}
else
{
for (int i = 1; i <= guesses.Count; i++)
{
if (i%4 == 0)
{
Console.Out.WriteLine(string.Format("{0}{1}{2}{3}", guesses[i - 4], guesses[i - 3],
guesses[i - 2], guesses[i - 1]));
position = i;
}
else
{
if (i == guesses.Count)
{
for (int j = position; j < i; j++)
{
Console.Out.Write(guesses[j]);
}
}
}
}
}

Categories