how to deal with exception in LINQ Select statement - c#

I have a LINQ query as follows
m_FOO = rawcollection.Select(p=> p.Split(' ')).Select(p =>
{
int thing = 0;
try
{
thing = CalculationThatCanFail(p[1]);
}
catch{}
return new { Test = p[0], FooThing = thing};
})
.GroupBy(p => p.Test)
.ToDictionary(p => p.Key, s => s.Select(q => q.FooThing).ToList());
So, the CalculationThatCanFail throws sometimes. I don't want to put null in and then filter that out with another Where statement later, and a junk value is equally unacceptable. Does anyone know how to handle this cleanly? Thanks.
EDIT: There's a good reason for the double Select statement. This example was edited for brevity

I'm not clear from question if you mean, you don't want to use null for FooThing or you don't want to use null for the entire anonymously typed object. In any case, would this fit the bill?
m_FOO = rawcollection.Select(p=> p.Split(' ')).Select(p =>
{
int thing = 0;
try
{
thing = CalculationThatCanFail(p[1]);
return new { Test = p[0], FooThing = thing};
}
catch
{
return null;
}
})
.Where(p => p != null)
.GroupBy(p => p.Test)
.ToDictionary(p => p.Key, s => s.Select(q => q.FooThing).ToList());

For these situations I use a Maybe type (similar to this one) for calculations that may or may not return a value, instead of nulls or junk values. It would look like this:
Maybe<int> CalculationThatMayHaveAValue(string x)
{
try
{
return CalculationThatCanFail(x);
}
catch
{
return Maybe<int>.None;
}
}
//...
var xs = ps.Select(p =>
{
Maybe<int> thing = CalculationThatMayHaveAValue(p[1]);
return new { Test = p[0], FooThing = thing};
})
.Where(x => x.FooThing.HasValue);

Related

C# implicit conversion error from generic list to Ienumerable

public IEnumerable<XdbActiveDiscipline> GetAllDisciplineDocs(string projectNumber)
{
try
{
// return _MigratorDBContext.XdbActiveDiscipline.Where(x => x.OtProjectNumber == projectNumber && x.IsProcessed==null).ToList();
var val = _MigratorDBContext.XdbActiveDiscipline.Where(x => x.OtProjectNumber == projectNumber && x.IsProcessed == null).OrderBy(x => x.DocumentReference).GroupBy(x => new { x.DocumentReference, x.DocumentRevisionNumber, x.DocumentRevisionObject })
.Select(g => new { g, count = g.Count() })
.SelectMany(t => t.g.Select(b => b)
.Zip(Enumerable.Range(1, t.count), (j, i) => new
{
j.OtProjectNumber,
rn = i
}))
.ToList();
return val;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
return val: Throws the error
And the code is written to add a rownumber() value at the last of the result set (i.e partition by)
Val is List of anonymos , not List < XdbActiveDiscipline > . Change return of the action to this:
return val.Select( i=> new XdbActiveDiscipline{
Id=i.Id,
.... and so on
}).ToArray();
Your return value is not typeof(XdbActiveDiscipline). As you can see the error says missing a cast? and the method expects an IEnumerable of XdbActiveDiscipline in return.
The element you are selecting on the linq query is an anonymous type different from the expected return, you can see that if you hover over the val when instantiating.

How do you call a function to an Linq List query that uses the exist function

I have this method with a linq statement below. I'm not a fan of multiple if statement and I'm trying to find what is the best way to not have these if statement and have a private method.
My field values is being set as such:
var fieldValues = await GetFields // then it's being passed to my method.
public static AppraisalContactBorrower BuildCoBorrower(List<LoanFieldValue> fieldValues) {
var coborrower = new AppraisalContactBorrower();
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.COBORRNAME")) {
coborrower.Name = fieldValues.First(v => v.FieldId == "CX.OS.AO.COBORRNAME").Value;
}
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
borrower.Zip = fieldValues.First(v => v.FieldId == "CX.OS.AO.BORRCONTACTZIP").Value;
}
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
borrower.Zip = fieldValues.First(v => v.FieldId == "CX.OS.AO.BORRCONTACTZIP").Value;
}
What I'm trying to do is instead of this:
coborrower.Name = fieldValues.First(v => v.FieldId == "CX.OS.AO.COBORRNAME").Value;
Is having something similar to this.
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
coborrower.Name = SETVALUE("CX.OS.AO.BORRCONTACTZIP")}
First, try using Enumerable.ToDictionary to have the field values grouped by FieldId, then use IDictionary.TryGetValue to get the existing values:
public static AppraisalContactBorrower BuildCoBorrower(List<LoanFieldValue> fieldValues) {
var groupedFieldValues = fieldValues.ToDictionary(f => f.FieldId)
var coborrower = new AppraisalContactBorrower();
if (groupedFieldValues.TryGetValue("CX.OS.AO.COBORRNAME", out var name)) {
coborrower.Name = name.Value;
}
if (groupedFieldValues.TryGetValue("CX.OS.AO.BORRCONTACTZIP", out var zip)) {
borrower.Zip = zip.Value;
}
}
Using Dictionary makes it faster to check the appropriate field existence as it is O(1) and with TryGetValue you combine two operations into one (existence check + obtaining the value).
Your two last statements are almost identitical. The equivalent of :
if (groupedFieldValues.TryGetValue("CX.OS.AO.COBORRNAME", out var name)) {
coborrower.Name = name.Value;
}
is:
coborrower.Name = fieldValues.FirstOrDefault(v => v.FieldId == "CX.OS.AO.COBORRNAME")
?? coborrower.Name;
In the original code, coborrower.Name is not updated if the field doesn't exist in the list.

linq C# why the value change

Easily say, why those two qqq are different in the breakpoint?
TodayInformation and YesterdayInformation actually are type of List<MyClass>
It is very strange that as usually, YesterdayInformation couldn't be any changes through this part, but actually YesterdayInformation is updated as TodayInformation I never use any pointer or reference?
var qqq = YesterdayInformation;
var TodayInformation = YesterdayInformation;
TodayInformation.Select(o =>
{
o.Signal = SignalpairList.Where(p => p.pair == o.pair).Select(p => p.signal).First();
o.SigmaMove = SigmaMovepairList.Where(p => p.pair == o.pair).Select(p => p.SigmaMove).First();
o.Date = Today;
return o;
}).ToList();
qqq = YesterdayInformation;
Because TodayInformation and YesterdayInformation are the same reference. You have a query of TodayInormation, but you modified each item in .Select method. Your query does something like:
var TodayInformation = YesterdayInformation;
foreach(var o in TodayInformation)
{
//you are modifying each item of TodayInformation collection
o.Signal = SignalpairList.Where(p => p.pair == o.pair).Select(p => p.signal).First();
o.SigmaMove = SigmaMovepairList.Where(p => p.pair == o.pair).Select(p => p.SigmaMove).First();
o.Date = Today;
}
I think you want this:
var TodayInformation = YesterdayInformation;
var result = TodayInformation.Select(o => new
{
Signal = SignalpairList.Where(p => p.pair == o.pair).Select(p => p.signal).First(),
SigmaMove = SigmaMovepairList.Where(p => p.pair == o.pair).Select(p => p.SigmaMove).First(),
Date = Today
}).ToList();
This query loops over the collection TodayInformation, loads the data to an anonymous object without modifying the original item, and set the result list into a variable.
I'm not exactly sure what you are saying or asking, but the code as-in is basically a loop updating the items.
This would be a lot clearer, and equivalent to your code.
var TodayInformation = YesterdayInformation;
foreach (var oin TodayInformation) {
o.Signal = SignalpairList.Where(p => p.pair == o.pair).Select(p => p.signal).First();
o.SigmaMove = SigmaMovepairList.Where(p => p.pair == o.pair).Select(p => p.SigmaMove).First();
o.Date = Today;
}

C# LINQ GroupJoin skip item

I have the following code
var profilesWithMailLists = profilesWithCf.GroupJoin(
profile2Maillist,
p => p.SqlId,
p2l => p2l.ProfileId,
(p, mailLists) =>
{
var p2lmongo = mailLists.Select<TProfile2MailList, Profile2MailList>(p2l =>
{
return new Profile2MailList
{
MailListId = p2l.MailListId,
Status = p2l.Status,
SubscriptionDate = p2l.SubscriptionDate
};
});
p.MailLists = p2lmongo.ToArray();
return p;
});
Is it possible to skip iteration and returning of profile if p2lmongo collection will contain 0 elements ?
Try using GroupBy and a Join instead of GroupJoin.
Some code to illustrate (not tested or type-checked):
var profilesWithMailListsAlt =
profilesWithCf.Join(
profile2Maillist.GroupBy(p2l => p2l.ProfileId),
p => p.SqlId,
p2lgroup => p2lgroup.Key,
(p, mailListGroup) =>
{
var p2lmongo = mailListGroup.Select<TProfile2MailList, Profile2MailList>(p2l =>
{
return new Profile2MailList
{
MailListId = p2l.MailListId,
Status = p2l.Status,
SubscriptionDate = p2l.SubscriptionDate
};
});
p.MailLists = p2lmongo.ToArray();
return p;
});
If that still doesn't do what you want, try explicitly excluding empty groups by changing
profile2Maillist.GroupBy(p2l => p2l.ProfileId)
to
profile2Maillist.GroupBy(p2l => p2l.ProfileId).Where(group => group.Any())
Just shooting from the hip here.

SelectMany Anonymous Type and Skip Iterations

I've been trying for a long time to find a "clean" pattern to handle a .SelectMany with anonymous types when you don't always want to return a result. My most common use case looks like this:
We have a list of customers that I want to do reporting on.
Each customer's data resides in a separate database, so I do a parallel .SelectMany
In each lambda expression, I gather results for the customer toward the final report.
If a particular customer should be skipped, I need to return a empty list.
I whip these up often for quick reporting, so I'd prefer an anonymous type.
For example, the logic may looks something like this:
//c is a customer
var context = GetContextForCustomer(c);
// look up some data, myData using the context connection
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return null;
This could be implemented as a foreach statement:
var results = new List<WhatType?>();
foreach (var c in customers) {
var context = GetContextForCustomer(c);
if (someCondition)
results.AddRange(myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 }));
}
Or it could be implemented with a .SelectMany that is pre-filtered with a .Where:
customers
.Where(c => someCondition)
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
})
.ToList();
There are problems with both of these approaches. The foreach solution requires initializing a List to store the results, and you have to define the type. The .SelectMany with .Where is often impractical because the logic for someCondition is fairly complex and depends on some data lookups. So my ideal solution would look something like this:
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
continue? return null? return empty list?
})
.ToList();
What do I put in the else line to skip a return value? None of the solutions I can come up with work or are ideal:
continue doesn't compile because it's not an active foreach loop
return null causes an NRE
return empty list requires me to initialize a list of anonymous type again.
Is there a way to accomplish the above that is clean, simple, and neat, and satisfies all my (picky) requirements?
You could return an empty Enumerable<dynamic>. Here's an example (though without your customers and someCondition, because I don't know what they are, but of the same general form of your example):
new int[] { 1, 2, 3, 4 }
.AsParallel()
.SelectMany(i => {
if (i % 2 == 0)
return Enumerable.Repeat(new { i, squared = i * i }, i);
else
return Enumerable.Empty<dynamic>();
})
.ToList();
So, with your objects and someCondition, it would look like
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return Enumerable.Empty<dynamic>();
})
.ToList();
Without knowing what someCondition and myData look like...
Why don't you just Select and Where the contexts as well:
customers
.Select(c => GetContextForCustomer(c))
.Where(ctx => someCondition)
.SelectMany(ctx =>
myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
EDIT: I just realized you need to carry both the customer and context further, so you can do this:
customers
.Select(c => new { Customer = c, Context = GetContextForCustomer(c) })
.Where(x => someCondition(x.Context))
.SelectMany(x =>
myData.Select(d => new { CustomerID = x.Customer, X1 = d.x1, X2 = d.x2 });
You can try following:
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return Enumerable.Empty<int>().Select(x => new { CustomerID = 0, X1 = "defValue", X2 = "defValue" });
})
.ToList();
All anonymous types with the same set of properties (the same names and types) are combined into one one anonymous class by compiler. That's why both your Select and the one on Enumerable.Empty will return the same T.
You can create your own variarion of SelectMany LINQ method which supports nulls:
public static class EnumerableExtensions
{
public static IEnumerable<TResult> NullableSelectMany<TSource, TResult> (
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
foreach (TSource item in source) {
IEnumerable<TResult> results = selector(item);
if (results != null) {
foreach (TResult result in results)
yield return result;
}
}
}
}
Now you can return null in the selector lambda.
The accepted answer returns dynamic. The cleanest would be to move the filtering logic into a Where which makes the whole thing look better in linq context. Since you specifically rule that out in the question and I'm not a fan of delegates written over multiple lines in a linq call I will try this, but one can argue its more hacky.
var results = new
{
customerID = default(int), //notice the casing of property names
x1 = default(U), //whatever types they are
x2 = default(V)
}.GetEmptyListOfThisType();
foreach (var customerID in customers) {
var context = GetContextForCustomer(customerID);
if (someCondition)
results.AddRange(myData.Select(x => new { customerID, x.x1, x.x2 }));
}
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
Notice the appropriate use of property names which is in accordance with other variable names, hence you dont have to write the property names a second time in the Select call.

Categories

Resources