I have a list of objects that I am trying to bind to a listview. I am sorting by two properties. The problem exists whereby some records may not have one of the properties. This is causing an error. I would like it to still bind the records that have the property.
IEnumerable<ERec> list = retailerList.Cast<ERec>();
lvwRetailStores.DataSource = list.OrderByDescending(r => r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"].ToString());
list.Where(r => r.Properties["RS_Partner_Type"] != null && r.Properties["RS_Title"] != null)
.OrderByDescending(r => r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"].ToString());
Or instead of != null, use whatever test the Properties collection has.
I've found that the ?? Operator works well. I use Parenthesis to evaluate for null,
For Example:
Datetime? Today = DateTimeValue
// Check for Null, if Null put Today's date
datetime GoodDate = Today ?? DateTime.Now
This same logic works in Lambda, just use parenthesis to ensure that the correct comparisons are used.
You can use a ternary expression in the lambda:
list.OrderByDescending(r => r.Properties["RS_Partner_Type"] == null ? null : r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"] == null ? null : r.Properties["RS_Title"].ToString());
Another common approach is to give the collection a suitable default value, and return that when the collection doesn't have a particular key. For instance, if Properties implements IDictionary,
public static class IDictionaryExtension {
public static TValue GetValue<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue default) {
TValue result;
return dict.TryGetValue(key, out result) ? result : dflt;
}
}
...
lvwRetailStores.DataSource = list.OrderByDescending(r => r.GetValue("RS_Partner Type", "").ToString())
.ThenBy(r => r.GetValue("RS_Title","").ToString());
Related
I have a list of type List<KeyValuePair<string, bool>>. This list contains 8 items. I also have a string line. I'm trying to check if line does not contain a word from the TKey (string) and the TValue is false.
I'm not using a dictionary because lst could potentially contain duplicates.
This should be true if line does not contain any TKey where the corresponding TValue is false.
It seems that neither Any nor All are suiting my needs here:
if (lst.Any(x => !line.Contains(x.Key) && x.Value == false)
{
// this is always true even if line does contain a TKey.
// I'm not exactly sure why.
}
if (lst.All(x => !line.Contains(x.Key) && x.Value == false)
{
// this is always false even if line does not contain a TKey.
// I think it would only be true if line contained *every* TKey?
}
Is there a way to do this?
One way to do it is to check that all words with Value of false are missing from the target string by filtering on the p.Value:
if (list.Where(p => !p.Value).All(w => !line.Contains(x.Key))) {
...
}
You could also fix your query:
if (lst.All(x => !line.Contains(x.Key) || x.Value) {
...
}
This requires that for each KVP at least one of the following were true:
Line does not contain the key, or
The value is true
This should work for you
if (!lst.Any(x => line.Contains(x.Key) && !x.Value))
{
//line does not contain any TKey where the corresponding TValue is false
}
My code below gives me a NullReferenceException and the stack trace tells me the problem is in the Count method, so I'm pretty sure at some point foo, bar or baz is null.
My code:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.ToDictionary(g => g.Key.ToString(),
g => g.Count(r => r.foo.bar.baz.dummy == "Success"));
I'm wondering what's a concise way to handle null cases.
I learn that in C# 6.0 I can just do foo?.bar?.baz?.dummy, however the project I'm working on isn't C# 6.0
A solution for <6.0 would be:
.Count(r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == "Success")
Exactly for complex constructs like the one above the null propagation operator was introduced.
Furthermore you could also refactor the expression into a private method:
private Expression<Func<Referral, bool>> Filter(string value)
{
return r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == value;
}
and use it as follows:
g => g.Count(Filter("Success"))
You can use the following extension methods.
public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class
where TInput : class
{
return o == null ? null : evaluator(o);
}
public static TResult Return<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator, TResult failureValue)
where TInput : class
{
return o == null ? failureValue : evaluator(o);
}
Their combination gives you a nice, readable API for handling nulls:
return foo
.With(o => o.bar)
.With(o => o.baz)
.Return(o => o.dummy, null);
The problem is that the ToDictionary method isn't actually done on the queryable - instead, you get the whole collection, and do the aggregation in your application, rather than on the DB server.
So instead of using ToDictionary directly, use Select first:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.Select(g => new { Key = g.Key.ToString(),
Count = g.Count(r => r.foo.bar.baz.dummy == "Success") })
.ToDictionary(i => i.Key, i => i.Count);
This will make sure the aggregation is done in the database (where you don't care about those nulls) and not in the C# code (where you get a NullReferenceException).
Of course, this assumes that the queryable you're using is a DB query (or, to be more precise, a queryable that supports aggregation and has ANSI SQL-like NULL semantics). If you have a different custom queryable, it's not going to help (unless you explicitly add those NULL semantics yourself).
// if null, use null
if(objectvariable == null)
{
// throw exception
}
// if not null
if(objectvariable != null)
{
// continue
}
Following is my Linq code, here we return a Dictionary<string,object>, while filling object value for each string key we internally fill the Dictionary<string,string>, which is FieldPropertyMappingin the code below
allCardColumnMappingFieldPropertyEntity
.GroupBy(column => (column.FieldDesc + column.OutputColumnId))
.ToDictionary(groupElement => groupElement.Key,
groupElement => (object)groupElement.Select(currentElement =>
{
currentElement.FieldPropertyMapping =
fieldPropertyEntity
.Where(field => field.OutputColumnId == currentElement.OutputColumnId)
.Where(field => field.FieldDesc == currentElement.FieldDesc)
.ToDictionary(property => property.PropertyDesc, property => property.PropertyValue);
return currentElement;
}).FirstOrDefault());
Challenge we have in the current code we cannot have currentElement.FieldPropertyMapping as null at any time even if the Where clause find no matching record it will always lead to empty dictionary, but we have a business requirement to make it null if it is empty. I have done the following modification to fulfill the condition, but probably there's a better way to achieve it in Linq code, any pointer / suggestion
Modified code
allCardColumnMappingFieldPropertyEntity
.GroupBy(column => (column.FieldDesc + column.OutputColumnId))
.ToDictionary(groupElement => groupElement.Key,
groupElement => (object)groupElement.Select(currentElement =>
{
List<DesignCardFieldProperty> localDesignCardFieldPropertyEntity = fieldPropertyEntity
.Where(field => field.OutputColumnId == currentElement.OutputColumnId)
.Where(field => field.FieldDesc == currentElement.FieldDesc).ToList();
if(localDesignCardFieldPropertyEntity.Any())
currentElement.FieldPropertyMapping = localDesignCardFieldPropertyEntity
.ToDictionary(property => property.PropertyDesc, property => property.PropertyValue);
return currentElement;
}).FirstOrDefault());
Try with simple extension method
public static class Extensions
{
public static IDictionary<TKey, TValue> NullIfEmpty<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null || !dictionary.Any())
{
return null;
}
return dictionary;
}
}
I am trying to fix these linq statements so that they wont error if the model is null.
For example:
model.FilterSet.Dispositions may be null. While model.FilterSet.GenderFilters may not.
My current linq statement gives error if there is a null so I tried to add .Where(item => item != null) in the DispositionFilters linq, but it gives me an error saying that this will always be true.
Here is my code:
var filters = new RespondentCSVFilters
{
CSVFilters = new CSVFilters
{
DispositionFilters = model.FilterSet.Dispositions.Where(item=>item != null).ToList().ConvertAll(item => (int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), item.ToString()))),
EthnicitiesFilters = model.FilterSet.Ethnicities.ConvertAll(item => (int)((Ethnicity)Enum.Parse(typeof(Ethnicity), item.ToString()))),
GenderFilters = model.FilterSet.Genders.ConvertAll(item => (int)((Gender)Enum.Parse(typeof(Gender), item.ToString()))),
SourcesFilters = model.FilterSet.Sources.ConvertAll(item => (int)((RespondentSource)Enum.Parse(typeof(RespondentSource), item.ToString()))),
}
};
I am not sure how to make the changes in order to handle nulls.
I tried to add .Where(item => item != null)
But this will only check each item for null, not the source ("Dispositions"). To check for null fluently (without adding a bunch of "if" statements), an EmptyIfNull extension can be helpful:
public static class Extensions
{
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> self)
{
return self == null ? Enumerable.Empty<T>() : self;
}
}
Now you can write ...
model.FilterSet.Dispositions.EmptyIfNull().Where( ... )
and if model.FilterSet.Dispositions is null, you won't get an exception, but the statement will evaluate to an empty set.
I have a Dictionary<int int>. When I check the keys of the Dictionary for a number and it is in it, I want it to return the number, else I want the linq query to return 0.
Something like the following, except working
var t = (from result in results
where result.Key == 3
select result.Key != null ? result.Value : 0).First();
Because the problem is that when there is no number in the list, the sequence contains no element, so you can't check with a null or a count.
Just use TryGetValue.
int i;
results.TryGetValue(3, out i);
If it finds it, i is set to the value. If not, i is default, which will be zero for int.
If you want another value besides default, you can do this:
int i;
if (!results.TryGetValue(3, out i))
{
i = 5; // or whatever other value you want;
}
And if you, like me, hate the out parameter style, you can create an extension method
public static class IDictionaryExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
{
T i;
dictionary.TryGetValue(key, out i);
return i;
}
}
and then you can call:
int i = dictionary.GetValueOrDefault(3);
and if you want to get fancier you can create another oveload of the extension:
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
T i;
return dictionary.TryGetValue(key, out i) ? i : defaultValue;
}
which can be called
int i = dictionary.GetValueOrDefault(3, 5);
Why not just
var t = results.ContainsKey(3) ? results[3] : 0;
and bypass the need for LINQ altogether?
return results.ContainsKey(key) ? results[key] : 0;
It sounds like results is your dictionary.
Dictionary<int int> results = new Dictionary<int,int>{{1,1},{3,3}};
int value;
results.TryGetValue (4, out value);
return value;
value is 0, because TryGetValue sets it to default(int), which of course is 0.
You can use linq if you want to type more, confuse people reading your code, and slow things down. This won't use the hashcode, so it is a slow lookup Big O(n).
var t = (from result in results
where result.Key == 3
select result.Key != null ? result.Value : 0).FirstOrDefault();
Try var t = (from result in results where result.Key == 3 select result.Value).FirstOrDefault();
Now, either you have a match in the where clause, so select will project the correct value, or you have an empty sequence. FirstOrDefault() then either returns the value (if it exists) or 0 (The default for integers.)
Select the value out and then:
ToArray().FirstOrDefault()