LINQ to Objects filtering - c#

I have a class, BillingProvider, which contains a list of Claims. Separately, I have a list of the claim numbers which contain errors. I want to exclude the claims with errors and, if all the claims under any BillingProvider have errors, then exclude the BillingProvider too. I've created a simplified mock-up of the situation. The LINQ query below excludes the errors but returns the BillingProvider multiple times.
class Program
{
class BillingProvider
{
internal string TaxId { get; set; }
internal List<Claim> Claims = new List<Claim>();
}
class Claim
{
internal int ClaimNumber { get; set; }
internal string ClaimDescr { get; set; }
}
private static void Main()
{
var allBillingProviders = new List<BillingProvider>
{
new BillingProvider
{
TaxId = "123456789",
Claims = new List<Claim>
{
new Claim {ClaimNumber = 1, ClaimDescr = "First Claim"},
new Claim {ClaimNumber = 2, ClaimDescr = "Second Claim"},
new Claim {ClaimNumber = 3, ClaimDescr = "Third Claim"}
}
},
new BillingProvider
{
TaxId = "012345678",
Claims = new List<Claim>
{
new Claim{ClaimNumber = 4, ClaimDescr = "Fourth Claim"},
new Claim{ClaimNumber = 5, ClaimDescr = "Fifth Claim"},
new Claim{ClaimNumber = 6, ClaimDescr = "Sixth Claim"},
new Claim{ClaimNumber = 7, ClaimDescr = "Seventh Claim"},
new Claim{ClaimNumber = 8, ClaimDescr = "Eighth Claim"}
}
}
};
// Set up errors
var errors = new List<int> {2, 5}; // Claims 2 and 5 have erros and should be excluded
var bpClaims = (from b in allBillingProviders
from c in b.Claims
where (!errors.Contains(c.ClaimNumber))
select b).ToList();
foreach (var bpc in bpClaims)
Console.WriteLine("Count of claims in {0} is {1}", bpc.TaxId, bpc.Claims.Count);
Console.ReadLine();
}
}

I would do this in two steps:
var bpClaims =
allBillingProviders.Select(x => new BillingProvider()
{
TaxId = x.TaxId,
Claims = x.Claims.Where(c => !errors.Contains(c.ClaimNumber)).ToList()
})
.Where(x => x.Claims.Any())
.ToList();

One way to use distinct, but since you may want to distinct by ClaimNumber since I think claim number will not be repeated you can use this library that has DistictBy
https://code.google.com/p/morelinq/
var bpClaims = (from b in allBillingProviders
from c in b.Claims
where (!errors.Contains(c.ClaimNumber))
select b).DistinctBy(c=>c.ClaimNumber).ToList();

You can get the count of the claim IDs without including the error claim IDs by doing this in your Console.WriteLine line:
Console.WriteLine("Count of claims in {0} is {1}", bpc.TaxId, bpc.Claims.Select(x=> x.ClaimNumber).ToList().Except(errors).Count());

Related

How to avoid Looping and refactor the Code in C#

My use case is, I have a List of orders that I need to post to external API.
but the conditions are that, I can post 5 order in one post call of API.
and these 5 orders have to be of same store and of deliveryWindows should be either morning or afternoon for all 5 orders.
I have written the below code but I am not happy with that, Can anyone Kindly help to refactor the below logic.
I have used 3 for loops to Loop through Deliverywindow and also for stores and for all the orders in the store.
Is there better approach/async Looping of the below/ having separate method calls.
Any suggestion is really helpful!
using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
static void Main(string[] args)
{
//In real time I will have 1000's of orders for one store and deliveryWindow (Morning)
var allOrders = GetProductOrders();
string[] deliveryWindows = new[] { "Morning", "AfterNoon" };
//Looping for Morning & Afternoon
foreach (var deliveryWindow in deliveryWindows)
{
//Getting Morning order in first run and afternoon order in second run
var OrderForWindow = allOrders.Where(x => x.DeliveryWindow.Equals(deliveryWindow));
//Getting All Unique Store (Will have StoreA, StoreB, etc)
List<string> Stores = OrderForWindow.Select(x => x.StoreName).Distinct().ToList();
foreach (var Store in Stores)
{
//Store releated order for that windown (morning/afternoon)
var StoreOrders = OrderForWindow.Where(order => order.StoreName.Equals(Store)).ToList();
//taking 10 items from StoreOrders
//Batch will pick 5 items at once
foreach (var orders in StoreOrders.Batch(5))
{
//storeOrder will have list of 5 order which all have same delivery window
//Post External call
}
}
}
}
public static List<ProductOrder> GetProductOrders()
{
List<ProductOrder> productOrder = new List<ProductOrder>()
{
new ProductOrder(){ ID = 1, DeliveryWindow ="Morning", StoreName = "StoreA", customerDetails = "Cust1"},
new ProductOrder(){ ID = 2, DeliveryWindow ="Morning", StoreName = "StoreA",customerDetails = "Cust2"},
new ProductOrder(){ ID = 3, DeliveryWindow ="Morning", StoreName = "StoreA",customerDetails = "Cust3"},
new ProductOrder(){ ID = 4, DeliveryWindow ="AfterNoon", StoreName = "StoreA",customerDetails = "Cust4"},
new ProductOrder(){ ID = 5, DeliveryWindow ="AfterNoon", StoreName = "StoreA",customerDetails = "Cust5"},
new ProductOrder(){ ID = 6, DeliveryWindow ="Morning", StoreName = "StoreB",customerDetails = "Cust6"},
new ProductOrder(){ ID = 7, DeliveryWindow ="Morning", StoreName = "StoreB",customerDetails = "Cust7"},
new ProductOrder(){ ID = 8, DeliveryWindow ="AfterNoon", StoreName = "StoreB",customerDetails = "Cust8"},
new ProductOrder(){ ID = 9, DeliveryWindow ="AfterNoon", StoreName = "StoreB",customerDetails = "Cust9"},
new ProductOrder(){ ID = 10, DeliveryWindow ="AfterNoon", StoreName = "StoreC",customerDetails = "Cust10"},
};
return productOrder;
}
}
public class ProductOrder
{
public int ID { set; get; }
public string StoreName { set;get;}
public string DeliveryWindow { set; get; }
public string customerDetails { set; get; }
public string ProductDetails { set; get; }
}
As pointed out, this post is a great resource to help you understand how to group against multiple keys.
Here's what that would look like in your case:
var allOrders = GetProductOrders();
var groupedOrders = from order in allOrders
// We group using an anonymous object
// that contains the properties we're interested in
group order by new
{
order.StoreName,
order.DeliveryWindow
};
// Access is straightforward:
foreach (var orderGroup in groupedOrders)
{
Console.WriteLine($"Group {orderGroup.Key.StoreName} {orderGroup.Key.DeliveryWindow}");
// The group is a list itself, so you can apply
// your Batch LINQ extension
foreach (var order in orderGroup)
{
Console.WriteLine(order.ID);
}
}

Unable mock an enumerable result from a task

I want to mock the UniteofWork.FileApproveOverrideRepo.GetAllOverrideApprovals method
Everything working based on the code below just mylist is empty.
public async Task<List<FileApprovalOverrideResponse>> GetAllOverrideApprovals(int cpId)
{
var overrideApprovals = await UnitOfWork.FileApprovalOverrideRepository.Get(t => t.CPId == cpId, null, t => t.ActionedByUser.Company);
var mylist = overrideApprovals.ToList();
return MapperInstance.Map<List<FileApprovalOverrideResponse>>(mylist);
}
And here is how I mock my method :
var mockedUnitOfWork = new Mock<IUnitOfWork>();
var mockedGenericRepoCPApproval = new Mock<IGenericAsyncRepository<FileApprovalOverride>>();
var listOfFileApprovalOverrride = new FileApprovalOverride[] {
new FileApprovalOverride()
{
FileApprovalOverrideId = 2,
FileId = 2,
CPId = 2,
ApprovalStatus = 3,
IntendedApproverType = (byte)approvType,
IsValid = true,
Reason = ""
},
new FileApprovalOverride()
{
FileApprovalOverrideId = 2,
FileId = 2,
CPId = 2,
ApprovalStatus = 3,
IntendedApproverType = (byte)approvType,
IsValid = true,
Reason = "",
}
};
var myenumerable = listOfFileApprovalOverrride;
mockedGenericRepoCPApproval.Setup(_ => _.Get(t => t.CPId == 1, null, t => t.ActionedByUser.Company)).ReturnsAsync(myenumerable);
mockedUnitOfWork.Setup(_ => _.FileApprovalOverrideRepository).Returns(mockedGenericRepoCPApproval.Object);
var cpApprovalService = new CpApprovalService(mockCPApprovalRepository.Object, new Mock<ICPRepository>().Object, new Mock<ICPService>().Object, new Mock<ICompanyRepository>().Object, new Mock<ICPLinkedCompanyService>().Object, new Mock<ICPExportService>().Object, new Mock<IEmailService>().Object, new Mock<ICPContactService>().Object, new Mock<ISystemUserRespository>().Object, new Mock<IUserManagementService>().Object, new Mock<IAuditLogService>().Object, new Mock<ISignCharterParty>().Object, new Mock<IEmailQueuer>().Object);
cpApprovalService.UnitOfWork = mockedUnitOfWork.Object;
cpApprovalService.MapperInstance = mapperInstanse;
return cpApprovalService;
I put a break point where I am creating the mock even there the myenumerable is correct and it has two members but when I run the test then the mylist is empty.

How to write a DISTINCT or GROUPBY LINQ statement using a strongly typed model in MVC

I have a Model called Survey as highlighted below;
I want to write a LINQ statement in my controller that will query the Survey Table and return distinct records by 'Topic'. Each distinct record will have an average rating. For example;
I'm new to LINQ and have a hard time when creating anything that is not basic.
My attempt;
var results = from s in db.Surveys
where s.Topic.Distinct()
select new Survey
{
SurveyId = s.SurveyId,
Category = s.Category.Name,
Topic = s.Topic,
Store1Rating= db.Surveys.Average(s => s.Score1).Value,
Store2Rating= db.Surveys.Average(s => s.Score2).Value
};
Thanks for your help in advance!
Try this
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var survey1 = new Survey() { Topic = "topic1", Store1Rate = 5, Store2Rate = 4 };
var survey2 = new Survey { Topic = "topic2", Store1Rate = 6, Store2Rate = 2 };
var survey3 = new Survey { Topic = "topic2", Store1Rate = 7, Store2Rate = 2 };
var survey4 = new Survey { Topic = "topic3", Store1Rate = 6, Store2Rate = 4 };
var survey5 = new Survey { Topic = "topic3", Store1Rate = 1, Store2Rate = 2 };
var survey6 = new Survey { Topic = "topic3", Store1Rate = 2, Store2Rate = 9 };
List<Survey> surveys = new List<Survey>() { survey1, survey2, survey3, survey4, survey5, survey6 };
var result = surveys.GroupBy(s => s.Topic).Select(s => new { Topic = s.Key, Rate1 = s.Average(a => a.Store1Rate), Rate2 = s.Average(a => a.Store2Rate) });
}
}
class Survey
{
public string Topic;
public int Store1Rate;
public int Store2Rate;
}
}
Dissapointed is correct
If you want to use this as a model you have to cast it to a list for example:
`var result = surveys.GroupBy(s => s.Topic).Select(s => new { Topic = s.Key, Rate1 = s.Average(a => a.Store1Rate), Rate2 = s.Average(a => a.Store2Rate) }).ToList();`
Then in your controller you do a:
`return View("ViewName", result)`

Grouping Linq Issue, can't get it right

public class Emp
{
public int EmpId { get; set; }
public string Type { get; set; }
public List<string> Email { get; set; }
}
I fetch data from database and put it in a list
List<Emp> employees= new List<Emp>();
// fill the list here via DB call
The list would have, please also note that Type field would always be same for same user but email would be different
employees[0] = new Emp{ EmpId = 1, Type = "User", Email = "one#test.com" };
employees[1] = new Emp{ EmpId = 1, Type= "User", Email = "two#test.com" };
employees[2] = new Emp{ EmpId = 2, Type = "Test", Email = "three#test.com" };
employees[3] = new Emp{ EmpId = 2, Type= "Test", Email = "four#test.com"};
employees[4] = new Emp{ EmpId = 3, Type = "Test", Email = "five#test.com" };
employees[5] = new Emp{ EmpId = 3, Type= "Test", Email = "six#test.com"};
employees[6] = new Emp{ EmpId = 4, Type= "User", Email = "seven#test.com"};
I'm trying to group Emp based on their EmpId
so the result should be a new list
Expected Result
Result = new Emp{ EmpId = 1, Type = "User", Email = "one#test.com", "two#test.com" };
new Emp{ EmpId = 2, Type = "Test", Email = "three#test.com", "four#test.com" };
new Emp{ EmpId = 3, Type = "Test", Email = "five#test.com", "six#test.com" };
new Emp{ EmpId = 4, Type = "User", Email = ""seven#test.com" };
//This is what I have done so far
// Please let me know if this is incorrect
var result = from emp in employees
group emp.Email by new { emp.EmpId, emp.Type } into g
select new { Key = g.Key, Type = g.Key.Type, Emails = g.ToList() };
// My problem comes here when I loop this result
foreach (var r in result)
{
Console.WriteLine(r.Key.EmpId + "--" + r.Key.Type);
//This is where I need to get all emails of the Employee which I grouped
// iF I IMPLEMENT FOREACH
foreach (var e in r.Emails)
{
//?? WHAT i DO HERE
//e.?? to get email
}
// OR iF I IMPLEMENT FOR LOOP
for(int i = 0 ; i< r.Emails.Count; i++)
{
Console.WriteLine("Inner Loop" + "--" + r.Key.EmpId + "--" + r.Key.Type + "--" + r.Emails[0].ToString()); // r.Emails[0].ToString() prints out System.Collections.Generic.List '1[System.String]
}
}
Please let me know if I mad eany mistake or there's other way to do this.
All I need is Group employees based on EmpID and also have their Type but grouped Emails.
Your group emp.Email by new { emp.EmpId, emp.Type } means that each element of the group will have a key of the anonymous type, and an "element type" of List<string>. You're then propagating that element type using Emails = g.ToList() in your select clause. Therefore I'd expect the type of r.Emails to be List<List<string>> (which you should be able to validate in Visual Studio by hovering over r.Emails.
You could handle that in your loop - or you could just flatten it in your select call, creating a new Emp:
select new Emp {
EmpId = g.Key.EmpId,
Type = g.Key.Type,
Emails = g.SelectMany(x => x).ToList()
};
Here the SelectMany call is just flattening the "sequence of lists" to a single sequence.
Maybe a slight change could be usefull: if you change the Email property from a list to string you could group the employees like that:
// Group by EmpId
var group = employees.GroupBy(e => e.EmpId);
and get the list for a single emp like this:
// Example get email List of first emp
group.First().Select(g => g.Email);
You could change your Emp class so that EMail is a string instead of a List<string>.
The foreach loop then becomes
foreach(string e in r.EMails){
//e holds the EMail
//do stuff
}
Try This:-
var query1 = from emp in employees
group emp.Email by new { emp.EmpId, emp.Type } into empgroup
select new
{
UserId = empgroup.Key.EmpId,
EmployeeType = empgroup.Key.Type,
EmaiIds = empgroup.SelectMany(x => x)
};
foreach (var x in query1)
{
Console.WriteLine(x.UserId);
Console.WriteLine(x.EmployeeType);
foreach (var emails in x.EmaiIds)
{
Console.WriteLine(emails);
}
}
void Main()
{
List<Emp> employees= new List<Emp>();
employees.Add(new Emp{ EmpId = 1, Type = "User", Email = "one#test.com" });
employees.Add(new Emp{ EmpId = 1, Type = "User", Email = "two#test.com" });
employees.Add(new Emp{ EmpId = 2, Type = "Test", Email = "three#test.com" });
employees.Add(new Emp{ EmpId = 2, Type = "Test", Email = "four#test.com" });
employees.Add(new Emp{ EmpId = 3, Type = "Test", Email = "five#test.com" });
employees.Add(new Emp{ EmpId = 3, Type = "Test", Email = "six#test.com" });
employees.Add(new Emp{ EmpId = 4, Type = "User", Email = "seven#test.com" });
var groupedList = from emp in employees
group emp.Email by new { emp.EmpId, emp.Type } into g
select new { Key = g.Key, Type = g.Key.Type, Emails = g.ToList() };
foreach (var result in groupedList)
{
//I'm using LINQPad to output the results
result.Key.EmpId.Dump();
foreach(var email in result.Emails)
{
email.Dump();
}
}
}
public class Emp
{
public int EmpId { get; set; }
public string Type { get; set; }
public string Email { get; set; }
}
My results are:
or...
void Main()
{
List<Emp> employees= new List<Emp>();
employees.Add(new Emp{ EmpId = 1, Type = "User", Emails = new List<string>(){"one#test.com"} });
employees.Add(new Emp{ EmpId = 1, Type = "User", Emails = new List<string>(){"two#test.com"} });
employees.Add(new Emp{ EmpId = 2, Type = "Test", Emails = new List<string>(){"three#test.com"} });
employees.Add(new Emp{ EmpId = 2, Type = "Test", Emails = new List<string>(){"four#test.com"} });
employees.Add(new Emp{ EmpId = 3, Type = "Test", Emails = new List<string>(){"five#test.com"} });
employees.Add(new Emp{ EmpId = 3, Type = "Test", Emails = new List<string>(){"six#test.com"} });
employees.Add(new Emp{ EmpId = 4, Type = "User", Emails = new List<string>(){"seven#test.com"} });
var groupedList = from emp in employees
group emp.Emails by new { emp.EmpId, emp.Type } into g
select new Emp {
EmpId = g.Key.EmpId,
Type = g.Key.Type,
Emails = g.SelectMany(x => x).ToList()
};
foreach (var result in groupedList)
{ //I'm using LINQPad to output
result.EmpId.Dump();
result.Emails.ForEach(e => e.Dump());
}
}
public class Emp
{
public int EmpId { get; set; }
public string Type { get; set; }
public List<string> Emails { get; set; }
}
My results are also:

c# Listitems dont add if exisit in array

I have an array of days (mon, tue, etc..) that I might not want to show, so when I create a list of days, if they are already in the "hiddendays", it doesn't add them.
// This would return for example 1, 2
var hDays = account.ScheduleHiddenDays.Split(',').Select(int.Parse).ToList();
var daySunday = new ViewModels.Day()
{
Id = 0,
Name = "Sunday"
};
var dayMonday = new ViewModels.Day()
{
Id = 1,
Name = "Monday"
};
var dayTuesday = new ViewModels.Day()
{
Id = 2,
Name = "Tueday"
};
var dayWednesday = new ViewModels.Day()
{
Id = 3,
Name = "Wednesday"
};
var dayThursday = new ViewModels.Day()
{
Id = 4,
Name = "Thursday"
};
var dayFriday = new ViewModels.Day()
{
Id = 5,
Name = "Friday"
};
var daySaturday = new ViewModels.Day()
{
Id = 6,
Name = "Saturday"
};
// This is where I need to check if they exist in hDays, it doesn't add them to this list. But how?
model.ScheduleHiddenDays = new List<ViewModels.Day>()
{
daySunday,
dayMonday,
dayTuesday,
dayWednesday,
dayThursday,
dayFriday,
daySaturday
};
Something along the lines of this:
var days = new List<ViewModels.Day>()
{
daySunday,
dayMonday,
dayTuesday,
dayWednesday,
dayThursday,
dayFriday,
daySaturday
};
model.ScheduleHiddenDays = days.Where(x => !hDays.Contains(x.Id)).ToList();

Categories