Skip Records by String Matching - c#

I'm looping through a large excel file full of Products. We want to only process rows where Brand = x, or Product = 'y'. The following code worked there was a predictable filter ie. Product1, however we are unsure what data the xsl file will hold and it could be something like "Product1 (buy me)" which wouldn't pass with this logic and the record would get ignored.
What technique can we use to match Product names with our filters? Regex, pattern matching etc. ? Or do I simply need to split the filter and loop through each? Seems like there shuold be a more elegant way.
private static bool SkipRecord(string strFilters, string key, DataRow row)
{
//include the record if it matches our filter
var strField = row[key].ToString();
bool skip = true;
if (strField != null && strField != "")
{
skip = !strFilters.ToLower().Contains(strField.ToLower());
}
return skip;
}
List<ResultRow> xlsRows = new List<ResultRow>();
foreach (DataRow row in dataTable.Rows)
{
if (SkipRecord(f.brandFlag, "Brand", row) && SkipRecord(f.productFlag, "Name", row))
continue;
}
appSettings.json
"CustomSettings": {
"BrandFlag": "Gibson|Fender|Jackson",
"ProductFlag": "Product 1|ProductTwo|Product3",
}

(One comment first, I think the first method is missing the
return skip;
line).
For the specific case you mentioned it would work if you changed the order of the "Contains" arguments:
skip = !strField.ToLower().Contains(strFilters.ToLower());

Related

c# wildcards not matching datatable column values

I am having an issue trying to match data table column names with a string containing a wildcard.
I have a data table with various column names which includes a set named like follows: "PsA", "PsB", "PsC", ect. I want to iterate through all the columns containing Ps in the title and use those titles to extract the data.
I currently have the following code which fails to return any matches. I have substituted a straight value in the if statement ("PsA") as a test, which works fine; however, when I use the wildcard, I get no matches. I have also tried Regex with no luck.
private void dfaSection_SelectedIndexChanged(object sender, EventArgs e)
{
string psText = null;
string colID = "Ps*";
offBox.Text = ""; //Clear textbox if a reselection occurs
psBox.Text = ""; //Clear textbox if a reselection occurs
info.offTitle = dfaSection.Text; //Set textbox from variable
dt = opr.findOffByTitle(info); //Get datatable from SQL database
if(dt.Rows.Count > 0)
{
offBox.Text = dt.Rows[0][5].ToString(); //Set textbox from datatable
foreach(DataColumn dc in dt.Columns) //Loop through datatable columns
{
if (dc.ColumnName.ToString() == colID) //Check that column title matches test string - Later addition--> && dt.Rows[0][dc].ToString() != null)
{
psText = dt.Rows[0][dc].ToString() + "\n\n"; //Add data from matched column to string variable
}
}
psBox.Text = psText; //Set textbox from variable
}
}
Edit: Issue fixed using .Contains(colID) Column names now being matched, string are now not being loaded to the psText variable, but I'll spend some time with that and get it working. Thanks Skaros Ilias.
I am not so failiar with C, but == for sure is not the right method.
you need to use the contains method. As I said, I am not familiar with it, but the docs have a good example of it.
String s = "This is a string.";
String sub1 = "this";
Console.WriteLine("Does '{0}' contain '{1}'?", s, sub1);
StringComparison comp = StringComparison.Ordinal;
Console.WriteLine(" {0:G}: {1}", comp, s.Contains(sub1, comp));
take a breakpoint a check what is column name and colID at failure moment.How are you going to solve problems, when don't even know values?
Use dc.ColumnName.ToString().StartsWith(). Contains() is good only untill string endswith.
As per this answer before : Regular expression wildcard
And here is sample of code :
private static bool Match(string pattern, string stringToCheck) {
var finalPattern = pattern.Replace("*", ".*?");
Regex regex = new Regex(finalPattern);
return regex.IsMatch(stringToCheck);
}

Linq OrderBy not sorting correctly 100% of the time

I'm using the Linq OrderBy() function to sort a generic list of Sitecore items by display name, then build a string of pipe-delimited guids, which is then inserted into a Sitecore field. The display name is a model number of a product, generally around 10 digits. At first it seemed like this worked 100% of the time, but the client found a problem with it...
This is one example that we have found so far. The code somehow thinks IC-30R-LH comes after IC-30RID-LH, but the opposite should be true.
I put this into an online alphabetizer like this one and it was able to get it right...
I did try adding StringComparer.InvariantCultureIgnoreCase as a second parameter to the OrderBy() but it did not help.
Here's the code... Let me know if you have any ideas. Note that I am not running this OrderBy() call inside of a loop, at any scope.
private string GetAlphabetizedGuidString(Item i, Field f)
{
List<Item> items = new List<Item>();
StringBuilder scGuidBuilder = new StringBuilder();
if (i != null && f != null)
{
foreach (ID guid in ((MultilistField)f).TargetIDs)
{
Item target = Sitecore.Data.Database.GetDatabase("master").Items.GetItem(guid);
if (target != null && !string.IsNullOrEmpty(target.DisplayName)) items.Add(target);
}
// Sort it by item name.
items = items.OrderBy(o => o.DisplayName, StringComparer.InvariantCultureIgnoreCase).ToList();
// Build a string of pipe-delimited guids.
foreach (Item item in items)
{
scGuidBuilder.Append(item.ID);
scGuidBuilder.Append("|");
}
// Return string which is a list of guids.
return scGuidBuilder.ToString().TrimEnd('|');
}
return string.Empty;
}
I was able to reproduce your problem with the following code:
var strings = new string[] { "IC-30RID-LH", "IC-30RID-RH", "IC-30R-LH", "IC-30R-RH"};
var sorted = strings.OrderBy(s => s);
I was also able to get the desired sort order by adding a comparer to the sort.
var sorted = strings.OrderBy(s => s, StringComparer.OrdinalIgnoreCase);
That forces a character-by-character (technically byte-by-byte) comparison of the two strings, which puts the '-' (45) before the 'I' (73).

Test for an empty DataRow in C#

what I'm trying to do: I have a large datatable, and I'm going through a list of strings where some of them are in the datatable and some aren't. I need to make a list of those that are, and count those that aren't.
This is my code part:
DataRow[] foundRows;
foundRows = DTgesamt.Select("SAP_NR like '%"+SAP+"%'");
if (AreAllCellsEmpty(foundRows[0]) == false && !(foundRows[0]==null))
{
list.Add(SAP);
}
else
{
notfound++;
}
public static bool AreAllCellsEmpty(DataRow row)
{
if (row == null) throw new ArgumentNullException("row");
for (int i = row.Table.Columns.Count - 1; i >= 0; i--)
{
if (!row.IsNull(i))
{
return false;
}
}
return true;
}
DTgesamt ist a large DataTable. "SAP" is a string that is in the first column of the DataTable, but not all of them are included. I want to count the unfound ones with the int "notfound".
The problem is, the Select returns an empty DataRow {System.Data.DataRow[0]} when it finds nothing.
I'm getting the errormessage Index out of array area.
The two statements in the if-clause are what I read on the internet but they don't work. With only the 2nd statement it just adds all numbers to the list, with the first it still gives this error.
Thanks for any help :)
check count of items in foundRows array to avoid IndexOutOfRange exception
foundRows = DTgesamt.Select("SAP_NR like '%"+SAP+"%'");
if (foundRows.Length > 0 && AreAllCellsEmpty(foundRows[0])==false)
list.Add(SAP);
else
notfound++;
The found cells cannot be empty. Your select statement would be wrong. So what you actually need is:
if (DTgesamt.Select("SAP_NR like '%"+SAP+"%'").Any())
{
list.Add(SAP);
}
else
{
notfound++;
}
You probably don't even need the counter, when you can calculate the missed records based on how many SAP numbers you had and how many results you got in list.
If you have an original list or array of SAP numbers, you could shorten your whole loop to:
var numbersInTable = originalNumbers.Where(sap => DTgesamt.Select("SAP_NR like '%"+sap+"%'").Any()).ToList();
var notFound = originalNumbers.Count - numbersInTable.Count;

C# DataRow Empty-check

I got this:
DataTable dtEntity = CreateDataTable();
drEntity = dtEntity.NewRow();
Then I add data to the row (or not).
Lots of code, really don't know if there's anything inside the row.
Depends on the input (i am importing from some files).
I'd like to do something like:
if (drEntity`s EVERY CELL IS NOT EMPTY)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
Is there some nice way to check if the DataRow's every cell is empty?
Or I should foreach, and check them one by one?
A simple method along the lines of:
bool AreAllColumnsEmpty(DataRow dr)
{
if (dr == null)
{
return true;
}
else
{
foreach(var value in dr.ItemArray)
{
if (value != null)
{
return false;
}
}
return true;
}
}
Should give you what you're after, and to make it "nice" (as there's nothing as far as I'm aware, in the Framework), you could wrap it up as an extension method, and then your resultant code would be:
if (datarow.AreAllColumnsEmpty())
{
}
else
{
}
I created an extension method (gosh I wish Java had these) called IsEmpty as follows:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i is DBNull);
}
The other answers here are correct. I just felt mine lent brevity in its succinct use of Linq to Objects. BTW, this is really useful in conjunction with Excel parsing since users may tack on a row down the page (thousands of lines) with no regard to how that affects parsing the data.
In the same class, I put any other helpers I found useful, like parsers so that if the field contains text that you know should be a number, you can parse it fluently. Minor pro tip for anyone new to the idea. (Anyone at SO, really? Nah!)
With that in mind, here is an enhanced version:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i.IsNullEquivalent());
}
public static bool IsNullEquivalent(this object value)
{
return value == null
|| value is DBNull
|| string.IsNullOrWhiteSpace(value.ToString());
}
Now you have another useful helper, IsNullEquivalent which can be used in this context and any other, too. You could extend this to include things like "n/a" or "TBD" if you know that your data has placeholders like that.
I prefer approach of Tommy Carlier, but with a little change.
foreach (DataColumn column in row.Table.Columns)
if (!row.IsNull(column))
return false;
return true;
I suppose this approach looks more simple and cleaner.
public static bool AreAllCellsEmpty(DataRow row)
{
if (row == null) throw new ArgumentNullException("row");
for (int i = row.Table.Columns.Count - 1; i >= 0; i--)
if (!row.IsNull(i))
return false;
return true;
}
I know this has been answered already and it's an old question, but here's an extension method to do the same:
public static class DataExtensions
{
public static bool AreAllCellsEmpty(this DataRow row)
{
var itemArray = row.ItemArray;
if(itemArray==null)
return true;
return itemArray.All(x => string.IsNullOrWhiteSpace(x.ToString()));
}
}
And you use it like so:
if (dr.AreAllCellsEmpty())
// etc
You could use this:
if(drEntity.ItemArray.Where(c => IsNotEmpty(c)).ToArray().Length == 0)
{
// Row is empty
}
IsNotEmpty(cell) would be your own implementation, checking whether the data is null or empty, based on what type of data is in the cell. If it's a simple string, it could end up looking something like this:
if(drEntity.ItemArray.Where(c => c != null && !c.Equals("")).ToArray().Length == 0)
{
// Row is empty
}
else
{
// Row is not empty
}
Still, it essentially checks each cell for emptiness, and lets you know whether all cells in the row are empty.
DataTable.NewRow will initialize each field to:
the default value for each DataColumn (DataColumn.DefaultValue)
except for auto-increment columns (DataColumn.AutoIncrement == true), which will be initialized to the next auto-increment value.
and expression columns (DataColumn.Expression.Length > 0) are also a special case; the default value will depend on the default values of columns on which the expression is calculated.
So you should probably be checking something like:
bool isDirty = false;
for (int i=0; i<table.Columns.Count; i++)
{
if (table.Columns[i].Expression.Length > 0) continue;
if (table.Columns[i].AutoIncrement) continue;
if (row[i] != table.Columns[i].DefaultValue) isDirty = true;
}
I'll leave the LINQ version as an exercise :)
AFAIK, there is no method that does this in the framework. Even if there was support for something like this in the framework, it would essentially be doing the same thing. And that would be looking at each cell in the DataRow to see if it is empty.
I did it like this:
var listOfRows = new List<DataRow>();
foreach (var row in resultTable.Rows.Cast<DataRow>())
{
var isEmpty = row.ItemArray.All(x => x == null || (x!= null && string.IsNullOrWhiteSpace(x.ToString())));
if (!isEmpty)
{
listOfRows.Add(row);
}
}
Maybe a better solution would be to add an extra column that is automatically set to 1 on each row. As soon as there is an element that is not null change it to a 0.
then
If(drEntitity.rows[i].coulmn[8] = 1)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
To delete null and also empty entries Try this
foreach (var column in drEntitity.Columns.Cast<DataColumn>().ToArray())
{
if (drEntitity.AsEnumerable().All(dr => dr.IsNull(column) | string.IsNullOrEmpty( dr[column].ToString())))
drEntitity.Columns.Remove(column);
}

compare value property

how can I compare value property of item to datatable column called Value? Please help me with the syntax
if ((String)item.Value.IndexOf((string)results("value") Stringcomparison.CurrentCultureIgnoreCase) > -1)
{
returnItems.Add(item);
}
Your question and comment seem to be asking different things, but in both cases the answer is probably to unpick the big long line of code into explanatory variables:
string valueFromTable = currentRow["Value"].ToString();
bool itemValueContainsValueFromTable = item.Value.IndexOf(valueFromTable, StringComparison.CurrentCultureIgnoreCase) >= 0;
bool itemValueEqualsValueFromTable = item.Value.Equals(valueFromTable, StringComparison.CurrrentCultureIgnoreCase);
if (/* whichever of these you are interested in */)
{
returnItems.Add(item);
}
(ignoring error cases here, specifically if item.Value is null)
Note that to get a value from the DataTable you will need to pick a row. If you want to see if the item matches any row, then iterate over the rows (foreach (var row in table.Rows)).

Categories