I have the following (working) code. It is very inelegant, and I think it can be refactored using Linq only and hence avoiding the foreach loop and having to rely on an external List<>. How to do this? Thanks
List<string> answerValues = new List<string>();
foreach (Fillings filling in fillings)
{
string answer = filling.Answers.Where(a => a.Questions == question)
.Select(a => a.Answer).FirstOrDefault();
if (!string.IsNullOrEmpty(answer)) answerValues.Add(answer);
}
IEnumerable<string> answerValues = fillings
.SelectMany(f => f.Answers)
.Where(a => a.Questions == question)
.Select(a => a.Answer)
.Where(ans => !string.IsNullOrEmpty(ans));
Or if you need a list:
IList<string> answerValues = fillings
.SelectMany(f => f.Answers)
.Where(a => a.Questions == question)
.Select(a => a.Answer)
.Where(ans => !string.IsNullOrEmpty(ans))
.ToList();
var answerValues = (
from f in fillings
from a in f.Answers
where a.Question == question
where !String.IsNullOrEmpty(a.Answer)
select a.Answer).ToList();
fillings.SelectMany(x => x.Answers.Where(a => a.Question == question)
.Select(a => a.Answer)
.FirstOrDefault())
.Where(x => !string.IsNullOrEmpty(x));
Related
I have a scenario where in case there is a specific boolean value satisfied (office_debit_total line) I can get amount directly from a column otherwise I need to calculate it by grouping some specific values, here's the code:
var result = products.Select(p => new ResponseDto()
{
customer_id = p.CustomerId,
office_debit_date = p.OfficeDebitDate.Value.ToString(),
office_debit_id = p.OfficeDebitId.ToString(),
office_debit_total = p.OfficeEnum == SomeEnum.ValueType ? p.OfficeAmount.ToString() : totalAmounts[p.OfficeDebitId].ToString(),
payment_method = p.PaymentMethod.Value.ToString(),
}).ToList();
As it's possible to be seen office_debit_total is calculated depending on enum value, and here's dictionary that I'm using to get grouped data:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key.OfficeDebitId, x => x.Sum(p => p.Amount));
But I have receiving following error message:
An item with the same key has already been added.
I've tried writing .ToLookup instead of .ToDictionary but that didn't helped me..
Thanks guys
Cheers
If your dictionary has only OfficeDebitId as key then you need to group by only by it:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
or use full anonymous object as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Or with value tuple as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => (p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod))
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Why not this:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
You might need it in this way (You can use value tuple):
Dictionary<(string OfficeDebitId, System.DateTime? OfficeDebitDate, Enumerations.PaymentMethod? PaymentMethod), decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => (x.Key.OfficeDebitId, x.Key.OfficeDebitDate, x.Key.PaymentMethod ), x => x.Sum(p => p.Amount));
I want to translate this into lambda syntax and can't seem to get it to work:
Grouping by two columns, select max on a different column, return list of complete complex object.
I am writing more text here to get past the validation on this form. How much text is needed until I am allowed to post this?
_clientpolicies = (from policy in
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
group policy by
new
{
PolicyReference = GetPolicyReference(policy),
PolicyType = policy.ProductInformation.PolicyTypeCode
}
into g
let maxPolicyInception = g.Max(p => p.InceptionDate)
from policyGroup in g
where policyGroup.InceptionDate == maxPolicyInception
select policyGroup).ToList();
I dont think there's a way of doing it in one line. So there's my try :
policyGroups=
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm
.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x => GetPolicyReference(x))
.ThenBy(x => x.ProductInformation.PolicyTypeCode)
.ToList();
var maxPolicyInception = policyGroups.Max(p => p.InceptionDate);
_clientpolicies = policyGroups
.Where(g => g.InceptionDate == maxPolicyInception)
.ToList();
_clientpolicies =
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x =>
new
{
PolicyReference = GetPolicyReference(x),
PolicyType = x.ProductInformation.PolicyTypeCode
},
(key, g) => g.OrderByDescending(gx => gx.InceptionDate).First()
SELECT DISTINCT MarketTemplateId AS MarketTemplateID
FROM Market
WHERE Market.FixtureId = ? And
Market.MarketTemplateId In
('MW3W', '1HTG', 'FTCS', 'HTFT', 'MTG2W', 'MW3W1H','FTCSALT')
how would i produce this in LINQ because i tried the below code and it doesn't work
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == InternalFixtureID_F.ToString())
.FirstOrDefault(p => MarketTemplateIds.Contains(p.MarketTemplateId));
List<string> MarketTemplateIds = new List<string>{ "MW3W", "1HTG", "FTCS", "HTFT", "MTG2W", "MW3W1H","FTCSALT" };
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == InternalFixtureID_F.ToString()
and MarketTemplateIds.Contains(p.MarketTemplateId ))
.Select( u => u.MaketTemplateId).Distinct();
How about
var id = InternalFixtureID_F.ToString();
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == id && MarketTemplateIds.Contains(p.MarketTemplateId))
.Select(m => m.MarketTemplateId)
.Distinct();
I have a fairly complicated query that would read from a table, then do group on CONTACT_ID, then select only those group with count of 1.
This query is fairly complicated and I have no idea how to optimize it in LINQ.
var linkTable = this.DB.Links
.Where(l=>l.INSTANCE_ID==123456 && l.CONTACT_ID.HasValue && l.ORGANISATION_ID.HasValue)
.Select(l => new { l.DEFAULT_LINKED_ORGANISATION, l.LINK_ID, l.CONTACT_ID });
var defaultOrganizationLinkQuery = linkTable
.Where(l => l.DEFAULT_LINKED_ORGANISATION)
.Select(l => l.LINK_ID);
var singleOrganizationLinkQuery = linkTable
.GroupBy(l => l.CONTACT_ID)
.Select(group => new
{
CONTACT_ID = group.Key,
contact_link_count = group.Count(),
LINK_ID = group.First().LINK_ID
})
.Where(l => l.contact_link_count == 1)
.Select(l => l.LINK_ID);
var merged = singleOrganizationLinkQuery.Union(defaultOrganizationLinkQuery);
I made shorter version, but I do not expect it to be faster. If it works and is not slower I would be satisfied:
var merged = this.DB.Links
.Where(l=>l.INSTANCE_ID==123456 && l.CONTACT_ID.HasValue && l.ORGANISATION_ID.HasValue)
.GroupBy(l => l.CONTACT_ID)
.SelectMany(s => s.Where(x => s.Count() == 1 || x.DEFAULT_LINKED_ORGANISATION)
.Select(link => link.LINK_ID));
var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv").Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderBy(x => x.Date)
.Where(x => x.Date >= LastCreatedDate);
i would like to know the value of the most recent x.Date
from this linq statement how can i get the most recent date?
please note that i do not need the filepath rather i need the DATE
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderBy(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.LastOrDefault();
or
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.FirstOrDefault();
Just reverse the order - also do the filtering before the ordering:
var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv").Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.Where(x => x.Date >= LastCreatedDate)
.OrderByDescending(x => x.Date)
.FirstOrDefault();
Instead I would suggest you use DirectoryInfo's GetFiles() instead which returns FileInfo instances so you don't have to grab the last write time manually:
var di = new DirectoryInfo(#"\\Pontos\completed\");
var file = di.GetFiles("*_*.csv")
.Where(f=> f.LastWriteTimeUtc >= LastCreatedDate)
.OrderByDescending(f => f.LastWriteTimeUtc)
.FirstOrDefault();
if(file!=null)
{
Console.WriteLine("Path: {0}, Last Write Time: {1}", file.FullName,
file.LastWriteTimeUtc);
}
F# has a handy MaxBy() function that I like to use; the C# implementation is trivial. It allows you to avoid the cost of sorting the sequence.
See this answer for more detail: https://stackoverflow.com/a/8759648/385844
usage:
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.Where(x => x.Date >= LastCreatedDate)
.MaxBy(x => x.Date);
you can use the method .Take(1);
Try this:
var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.FirstOrDefault();
The changes to your statement are the sorting (OrderByDescending instead of OrderBy) to put the newest date "on top" and FirstOrDefault which will select the top, single item from the collection and should result in null if the collection is empty.
To get more file properties you could modify your anonymous object to include more properties, thusly:
var filePath = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = File.GetLastWriteTime(p), CreatedDate = File.GetCreationTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= DateTime.Now)
.FirstOrDefault();
Console.WriteLine(filePath.Date);
Console.WriteLine(filePath.Path);
Console.WriteLine(filePath.CreatedDate);
Or more succinctly (no need for an anonymous object) you could do this:
var filePath = new DirectoryInfo(#"\\Pontos\completed\").GetFiles("*_*.csv")
.Select(p => p)
.OrderByDescending(p => p.CreationTime)
.Where(x => x.CreationTime >= DateTime.Now)
.FirstOrDefault();
Console.WriteLine(filePath.CreationTime);
Console.WriteLine(filePath.FullName);
As you're using LinqToObjects, if performance is a consideration, you should perhaps consider implementing a MaxBy type method, instead of using OrderBy combined with FirstOrDefault.
I'll find you an implementation. [no need... see #phoog's answer]