Min within a Lambda on a single Line - c#

Just looking for the nicest way to handle the following line and to keep it to one line...
if (int.TryParse(data.Min(x => x.ItemArray[i].ToString()), out result))
contained in the following routine...
var data = model.Data.AsEnumerable();
for (int i = 0; i < model.Data.Columns.Count; i++)
{
int result;
if (int.TryParse(data.Min(x => x.ItemArray[i].ToString()), out result))
{
model.Minimum.Add(DataTableUtility.MinValue(result));
}
}
This works fine until the ItemArray contains rows with no values (empty objects). Is there is simple way to keep this to a single line and handle this situation?

There are methods defined in the DataRow and DataColumn classes that you really should be using. Assuming each column in your table is a nullable int, you could do this:
var mins =
from row in table.AsEnumerable()
select (from DataColumn c in table.Columns
where !row.IsNull(c)
select row.Field<int>(c)).Min();
If you have mixed types, check the type and only accept ints:
var mins =
from row in table.AsEnumerable()
select (from DataColumn c in table.Columns
where !row.IsNull(c)
where c.DataType == typeof(int)
select row.Field<int>(c)).Min();
If you're actually storing integers as strings... then you have other problems... Then you have to parse it out. Just don't try to be clever. Create a method to try and parse out the values and return a value on success, 0 (or null) otherwise.
int? TryConvertInt32(obj value)
{
try { return Convert.ToInt32(value); } catch { return default(int?); }
}
var mins =
from row in table.AsEnumerable()
select row.ItemArray.Select(v => TryConvertInt32(v) ?? 0).Min();
et cetera...

I realised the conversion to string was a bracket early causing it to min by the string value. After this of course the casting of more complex types such as double needed further handling. Couldn't get it completely on one line, but the working solution (as a hybrid to some of these suggestions is as follows...
var data = model.Data.AsEnumerable();
for (int i = 0; i < model.Data.Columns.Count; i++)
{
int result;
if (int.TryParse(data.Min(x => TryConvertInt32((x.ItemArray[i] is System.DBNull || x.ItemArray[i] == null ? int.MaxValue : x.ItemArray[i]))).ToString(), out result))
{
model.Minimum.Add(DataTableUtility.MinValue(result));
}
}
with static "handler" as suggested above..
private static int? TryConvertInt32(object o)
{
try
{
return Convert.ToInt32(o);
}
catch
{
return default(int?);
}
}

You don't want to do this...
The simple reason, WHY you shouldn't do this?
Because this does not convey intent. What on earth is ItemArray, why are you .ToStringing it? Are you sure you don't want to do a safe cast? What about culture? How should you parse the string "1,234" (on the continent, that is "one point two three four", whilst an english speaker would parse that as "one thousand two hundred and thirty four")?
Are you sure you don't want
var results = from i in Enumerable.Range(0, model.Data.Columns.Count)
where data.Min(x => x.ItemArray.OfType<int>().DefaultIfEmpty())
select temp;

Easy:
if (int.TryParse(data.Min(x => (x.ItemArray[i] ?? int.MaxValue).ToString()), out result))
Just add the biggest value possible for int, as you are looking for min values those will be discarded.
EDIT:
So, if I understand then you will have ANY type checked there, so this is better:
if (int.TryParse(data.Min(x => (x.ItemArray[i] ?? "").ToString()), out result))
In this way on null items TryParse will fail.
EDIT:
Support for dbnull:
if (int.TryParse(data.Min(x => (x.ItemArray[i] is System.DBNull || x.ItemArray[i] == null ? "" : x.ItemArray[i]).ToString()), out result))

Related

How to check DBNull value in Linq query results

I am using the below code and trying to group by Currency and Code. After this I am trying to loop through the result set.
But the issue is while looping through the results, at the end I am getting the below exception on the for each statement:
Object cannot be cast from DBNull to other types.
DataTable dt = new DataTable();
var result = from r in dt.AsEnumerable()
result r by new
{
currency = r.Field<String>("CURRENCY"),
Code = r.Field<String>("CODE")
}
into grp
select new
{
currency = grp.Key.currency,
Code = grp.Key.Code,
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"]))
};
foreach (var obj in result)
{
String sCurr =obj.currency;
String Code = obj.Code;
string amount= obj.amount.ToString());
}
Please help me to resolve this issue.
Something like
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"] == DBNull.Value ? 0 : x["AMOUNT"]));
If that is the line that is giving you the problem.
The way to find the number of CELLS with DBNulls for a specific column:
int numOfEmptyColA = MyDataTable.AsEnumerable().Where(p=>p.IsNull("ColA")).Count();
This:
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"]))
will not work as you expect. If x["AMOUNT"] is DBNull.Value instead of a valid double, the conversion will fail with an exception. Instead, try:
amount = grp.Sum(x.Field<double?>("AMOUNT"))
if you expect that field to be a double. Sum will treat the null values as zero, per MSDN.
I'm not sure which one is getting the error, but you can can compare it to DBNull.Value like so.
String sCurr = obj.currency == DBNull.Value ? "" : obj.currency;

compare none value by linq

I have an integer column(not null) in a sql server 2008 database/table, I want to retrieve it.
// go to the table and get the largest number, if none exists, use 0.
int iNumber = iContext.DetailsRecords.Max(x => x.Number); // from entity
But at the very beginning, the table is empty. I want to set is as 0.
How to change the code?
If you don't want to check if the DetailsRecords is null, but rather handle if the source sequence is empty, then use this answer (I adjusted it a bit differently for your LINQ usage):
Max or Default?
int iNumber = iContext.DetailsRecords.Select(x => (int?)x.Number).Max() ?? 0;
Enumerable.Max Method (IEnumerable<Int32>): InvalidOperationException -
source contains no elements.
Enumerable.Max Method (IEnumerable<Nullable<Int32>>): If the source sequence is empty or contains only values that are null, this function returns null.
You can use DefaultIfEmpty for this. If the sequence is empty it will return the provided item as the only item in the sequence, if not it will return the original sequence.
IEnumerable<int> numbers = new int [] { };
numbers.DefaultIfEmpty(0).Max();
try this:
int iNumber = iContext.DetailsRecords==null? iContext.DetailsRecords.Max(x => x.Number) : 0;
or
int iNumber = iContext.DetailsRecords.Any()? iContext.DetailsRecords.Max(x => x.Number) : 0; if table is not null and contains 0 records.
Use the Any function to see if there are any records:
int iNumber = iContext.DetailsRecords.Any()
? iContext.DetailsRecords.Max(x => x.Number)
: 0;
I know this already has an accepted answer, but another good way would just be FirstOrDefault().
int iNumber = iContext.DetailsRecords.OrderByDescending(x => x.Number).FirstOrDefault();
I use this way often and also can let you setup a new object if the result was null.
MyObject m = mySearch.GetItems.Where(a => a.id == id).FirstOrDefault() ?? new MyObject();

Check Linq Result for value

How can I check the results of LINQ query for a specific string value?
I have the following linq query:
IEnumerable<DataRow> rows = searchParamsTable.AsEnumerable()
.Where(r => r.Field<String>("TABLE") == tableNumbers[i].ToString()
&& r.Field<String>("FIELD ") == fieldName[i]);
I want to see if the result of that query contains a string(passed in form a text box) "wildcardSearchString".
var searchRows =
rows.Where(tr => tr.ItemArray
.Any(ti => ti.ToString().IndexOf("wildcardSearchString", StringComparison.CurrentCultureIgnoreCase) > 0))
This will go through each of the rows that was returned, and see if "wildcardSearchString" is in the rows item string representation (ignoring case). Here's the problem though, this won't get you wildcard search support, so you'll have to figure that one out yourself. You can try to use Regex, which would require a slight modification:
string searchPattern = "some*string".Replace("*", ".*");
var searchRows =
rows.Where(tr => tr.ItemArray
.Any(ti => Regex.IsMatch(ti.ToString(), searchPattern)))
Hope that helps. Just be warned that if they decide to try supplying a Regex pattern than this might really mess up whatever they were searching for, so you just need to be careful of input.
try with this code
DataRow[] array = rows.ToArray();
array.Contains(yourIndex, yourTextBox.Text);
Add this extension
public static bool Contains(this DataRow[] dataRows, string value, int index)
{
foreach(var row in dataRows)
{
if(row[index].ToString().Contains(value))
{
return true;
}
}
return false;
}
Boolean found = false;
foreach(Datarow d in rows)
{
foreach(object o in d.ItemArray)
{
if(o.ToString().Contains("test")
{
found=true;
break;
}
}
}
Do you mean something like this?
I don't know if you're aware of the built-in search capabilities of a DataTable? You could use its Select method:
DataRow[] rows = searchParamsTable
.Select("TABLE = 'Table1' AND FIELD like '%wildcardSearchString%'");
Linq is OK but not always required :).

Getting a cell from DataTable.Row.ItemArray with Linq

I have the following ItemArray:
dt.Rows[0].ItemArray.. //{0,1,2,3,4,5}
the headers are : item0,item1,item2 etc..
So far, to get a value from the ItemArray I used to call it by an index.
Is there any way to get the value within the ItemArray with a Linq expression based on the column name?
Thanks
You can also use the column-name to get the field value:
int item1 = row.Field<int>("Item1");
DataRow.Item Property(String)
DataRow.Field Method: Provides strongly-typed access
You could also use LINQ-to-DataSet:
int[] allItems = (from row in dt.AsEnumerable()
select row.Field<int>("Item1")).ToArray();
or in method syntax:
int[] allItems = dt.AsEnumerable().Select(r => r.Field<int>("Item1")).ToArray();
If you use the Item indexer rather than ItemArray, you can access items by column name, regardless of whether you use LINQ or not.
dt.Rows[0]["Column Name"]
Tim Schmelter's answer is probably what you are lookin for, just to add also this way using Convert class instead of DataRow.Field:
var q = (from row in dataTable.AsEnumerable() select Convert.ToInt16(row["COLUMN1"])).ToArray();
Here's what I've come up with today solving a similar problem. In my case:
(1)I needed to xtract the values from columns named Item1, Item2, ... of bool type.
(2) I needed to xtract the ordinal number of that ItemN that had a true value.
var itemValues = dataTable.Select().Select(
r => r.ItemArray.Where((c, i) =>
dataTable.Columns[i].ColumnName.StartsWith("Item") && c is bool)
.Select((v, i) => new { Index = i + 1, Value = v.ToString().ToBoolean() }))
.ToList();
if (itemValues.Any())
{
//int[] of indices for true values
var trueIndexArray = itemValues.First().Where(v => v.Value == true)
.Select(v => v.Index).ToArray();
}
forgot an essential part: I have a .ToBoolean() helper extension method to parse object values:
public static bool ToBoolean(this string s)
{
if (bool.TryParse(s, out bool result))
{
return result;
}
return false;
}

LINQ query null exception when Where returns 0 rows

I have the following LINQ method that works as expected except if there are No Rows Found then I get a Null Exception. I am struggling on how I modify this to return 0 if that occurs.
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return (tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.DefaultIfEmpty()
.Max(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
);
}
}
Thanks
I tried one of Jon Skeet's suggestions, below, and now I get Unsupported overload used for query operator 'DefaultIfEmpty'
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.Select(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.DefaultIfEmpty(0)
.Max();
}
}
You're using
.Where(...)
.DefaultIfEmpty()
which means if there are no results, pretend it's a sequence with a single null result. You're then trying to use that null result in the Max call...
You can probably change it to:
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...)) ?? 0;
This uses the overload finding the maximum of int? values - and it returns an int? null if there were no values. The ?? 0 then converts that null value to 0. At least, that's the LINQ to Objects behaviour... you'll have to check whether it gives the same result for you.
Of course, you don't need to use the ?? 0 if you're happy to change the method signature to return int? instead. That would give extra information, in that the caller could then tell the difference between "no data" and "some data with a maximum value of 0":
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...));
Another option is to use the overload of DefaultIfEmpty() which takes a value - like this:
return tGreenSheet.Where(gs => ...)
.Select(gs => Convert.ToInt32(...))
.DefaultIfEmpty(0)
.Max();
In situations like this when there may or may not be a matching item, I prefer to return an object rather than a value type. If you return a value type, you have to have some semantics about what value means "there is nothing here." I would change it to return the last invoice, then (when it is non-null) get the invoice number from the invoice. Add a method to the class to return the numeric invoice number from the string.
public static tbleGreenSheet GetLastInvoice(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
return context.GetTable<tblGreenSheet>()
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.OrderByDescending(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.FirstOrDefault();
}
}
public class tbleGreenSheet
{
....
public int NumericInvoice
{
get { return Convert.ToInt32(InvoiceNumber.Substring(6, InvoiceNumber.Length)); }
}
...
}
Used as
var invoice = Foo.GetLastInvoice( 32 );
if (invoice != null)
{
var invoiceNumber = invoice.NumericInvoice;
...do something...
}
else
{
...do something else...
}
I had a remarkably similar experience with IQueryable<T> and NHibernate. My solution:
public static TExpr MaxOrDefault<TItem, TExpr>(this IQueryable<TItem> query,
Expression<Func<TItem, TExpr>> expression) {
return query.OrderByDescending(expression).Select(expression).FirstOrDefault();
}
The only drawback is that you are stuck with the standard default value, instead of getting to specify one.

Categories