Getting an array of dates (yearly and absolute) - c#

I have the following 2 functions + 1 in progress:
public static IEnumerable<LockedDate> GetAllByEmployee(int employeeID)
{
var v = LockedDates.GetAll();
return from p in v where p.EmployeeID == employeeID select p;
}
public static IEnumerable<LockedDate> GetAllByCompany()
{
var v = LockedDates.GetAll();
return from p in v where p.EmployeeID == null select p;
}
public static List<LockedDate> GetAllForEmployee(int employeeID)
{
var empList = GetAllByEmployee(employeeID);
var compList = GetAllByCompany();
//What do???
return from p in empList and from q in compList where p and q are not duplicate . toList()
}
These use LINQ-SQL.
A LockedDate has a Date and an IsYearly bool. If IsYearly is true then the year should not be considered. Time should never be considered.
I now need a function that will include all the LockedDates that the employee has and the company ones into one list, without any duplicates. So if IsYearly && dd/mm are == or !IsYearly && dd/mm/yyyy are == then there is a duplicate. What might be an efficient, non-naive way of doing this?
Thanks

var yearly = source.Where(p => p.IsYearly)
.GroupBy(p => new { Month = p.Date.Month, Day = p.Date.Day })
.Select(g => g.First());
var nonYearly = source.Where(p => !p.IsYearly)
.GroupBy(p => p.Date.Date)
.Select(g => g.First());
return yearly.Union(nonYearly).ToList();
source could be done easily by Union method:
var source = GetAllByEmployee(employeeID).Union(GetAllByCompany());

cant you do this
var v = LockedDates.GetAll();
return from p in v where p.EmployeeID == null || p.EmployeeID == employeeID select p;

Related

Aggregate of string on anonymous type

I want to concat multiple string value into single string with comma separated,i tried using aggregate function but it shows cannot convert string to how to fix this issue,
I tried below code
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = ((from st in WYNKContext.SurgeryTran.
Where(x => x.SurgeryID == e.SurgeryID)
select new
{
desc = icdmaster
.Where(x => x.ID ==
st.IcdSpecialityCode).Select(x =>
x.SpecialityDescription).FirstOrDefault(),
}).ToList()).Aggregate((a, b) => a.desc + "," + b.desc),
}).ToList();
I want Output like inside surgery property like = string1,string 2 ,etc....
without using aggregate i am getting as count in Surgery Property
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList(),
}).ToList();
also tried string join :
Surgery = string.Join(",", (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList()),
but in output i am getting like this
Surgery ={ icd = CORNEA },{ icd = CATARACT/IOL }
can some one tell what i did wrong in string.join.....
The string class has a static method named Join, which takes in a collection of items and a string to join them with, which should work for you here.
If I'm reading your code correctly, it would look something like this:
Surgery = string.Join(",", WYNKContext.SurgeryTran
.Where(surgTran => surgTran.SurgeryID == e.SurgeryID)
.Select(surgTran => icdmaster
.Where(icd => icd.ID == surgTran.IcdSpecialityCode)
.Select(icd => icd.SpecialityDescription)
.FirstOrDefault())),

Improve the time complexity of current Linq queries

I have the following lists:
RakeSnapshots, ProductMovements
Aim is to process the both and get the count of elements that match a condition, as follows:
Consider RakeSnapshots with StatusCode == "Dumping"
Consider ProductMovement with Status == "InProgress"
Fetch the count of all elements both lists, which meet the condition RakeSnapshots.RakeCode equal to ProductMovements.ProductCode
Following are my current options:
// Code 1:
var resultCount = ProductMovements.Where(x => RakeSnapshots
.Where(r => r.StatusCode == "Dumping")
.Any(y => y.RakeCode == x.ProductCode &&
x.Status == "InProgress"))
.Count();
// Code 2:
var productMovementsInprogress = ProductMovements.Where(x => x.Status == "InProgress");
var rakeSnapShotsDumping = RakeSnapshots.Where(r => r.StatusCode == "Dumping");
var resultCount = productMovementsInprogress.Zip(rakeSnapShotsDumping,(x,y) => (y.RakeCode == x.ProductCode) ? true : false)
.Where(x => x).Count();
Challenge is both the codes are O(n^2) complexity, is there a way to improve it, this will hurt if the data is very large
You can use an inner join to do this:
var dumpingRakeSnapshots = rakeSnapshots.Where(r => r.StatusCode == "Dumping");
var inProgressProductMovements = productMovements.Where(p => p.Status == "InProgress");
var matches =
from r in dumpingRakeSnapshots
join p in inProgressProductMovements on r.RakeCode equals p.ProductCode
select r;
int count = matches.Count(); // Here's the answer.
Note that (as Ivan Stoev points out) this only works if RakeCode is the primary key of RakeSnapshots.
If it is not, you will have to use a grouped join.
Here's the Linq query syntax version that you should use in that case, but note that this is exactly the same as Ivan's answer (only in Linq query form):
var matches =
from r in dumpingRakeSnapshots
join p in inProgressProductMovements on r.RakeCode equals p.ProductCode into gj
select gj;
For completeness, here's a compilable console app that demonstrates the different results you'll get if RakeCode and ProductCode are not primary keys:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class RakeSnapshot
{
public string StatusCode;
public string RakeCode;
}
class ProductMovement
{
public string Status;
public string ProductCode;
}
sealed class Program
{
void run()
{
var rakeSnapshots = new List<RakeSnapshot>
{
new RakeSnapshot {StatusCode = "Dumping", RakeCode = "1"},
new RakeSnapshot {StatusCode = "Dumping", RakeCode = "1"},
new RakeSnapshot {StatusCode = "Dumping", RakeCode = "2"}
};
var productMovements = new List<ProductMovement>
{
new ProductMovement {Status = "InProgress", ProductCode = "1"},
new ProductMovement {Status = "InProgress", ProductCode = "2"},
new ProductMovement {Status = "InProgress", ProductCode = "2"}
};
var dumpingRakeSnapshots = rakeSnapshots.Where(r => r.StatusCode == "Dumping");
var inProgressProductMovements = productMovements.Where(p => p.Status == "InProgress");
// Inner join.
var matches1 =
from r in dumpingRakeSnapshots
join p in inProgressProductMovements on r.RakeCode equals p.ProductCode
select r;
Console.WriteLine(matches1.Count());
// Grouped join.
var matches2 =
from r in dumpingRakeSnapshots
join p in inProgressProductMovements on r.RakeCode equals p.ProductCode into gj
select gj;
Console.WriteLine(matches2.Count());
// OP's code.
var resultCount =
productMovements
.Count(x => rakeSnapshots
.Where(r => r.StatusCode == "Dumping")
.Any(y => y.RakeCode == x.ProductCode && x.Status == "InProgress"));
Console.WriteLine(resultCount);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
Sounds like Group Join which (as well as Join) is the most efficient LINQ way of correlating two sets:
var resultCount = ProductMovements.Where(p => p.Status == "InProgress")
.GroupJoin(RakeSnapshots.Where(r => r.StatusCode == "Dumping"),
p => p.ProductCode, r => r.RakeCode, (p, match) => match)
.Count(match => match.Any());
The time complexity of the above is O(N+M).
Normally, with an O(N^2), you'd look to create an intermediate 'search' data structure which speeds up the lookup. Something like a hash table for O(1) access, or a sorted list for O(log N) access.
Technically, you have two different lists, so the actual order would be O(P.R), where P is the number of product movements, and R is the number of rake snapshots.
In your case, this is your original code;
var resultCount = ProductMovements
.Where(x => RakeSnapshots
.Where(r => r.StatusCode == "Dumping")
.Any(y => y.RakeCode == x.ProductCode &&
x.Status == "InProgress"))
.Count();
Is O(P.R) because for each P, the inner where clause is looping through every R. I'd look to creating a Dictionary<T> or HashSet<T>, then transforming your code to something like
var rakeSnapshotSummary = ... magic happens here ...;
var resultCount = ProductMovements
.Where(x => rakeSnapshotSummary[x.ProductCode] == true)
.Count();
In this way, creating the snapshot is O(R), lookup into the data structure is O(1), and creating the result is O(P), for a much healthier O(P+R). I thing that's is as good as it can be.
So my suggestion for your indexing routine would be something like;
var rakeSnapshotSummary = new HashSet<string>(RakeSnapshots
.Where(r => r.StatusCode == "Dumping")
.Select(r => r.RakeCode));
This creates a HashSet<string> which will have O(1) time complexity for testing existance of a rake code. Then your final line looks like
var resultCount = ProductMovements
.Where(x => x.Status == "InProgress" && rakeSnapshotSummary.Contains(x.ProductCode))
.Count();
So overall, O(P+R) or, roughly, O(2N) => O(N).

Trying to order a query by using a variable from a different query

So I tried doing this:
public List<User> GetLeaderBoard()
{
SuperGoalDataClassesDataContext myDB = new SuperGoalDataClassesDataContext();
var userResults = (from u in myDB.Users
orderby (GetUserPoints(u.userID))
select u).Take(100);
List<User> users = new List<User>();
foreach (var usr in userResults)
{
if (usr.myPoints > 0)
users.Add(usr);
}
return users;
}
public int? GetUserPoints(int userId)
{
SuperGoalDataClassesDataContext myDB = new SuperGoalDataClassesDataContext();
var codeResults = (from tc in myDB.TriviaCodes
where tc.userID == userId
select tc);
return codeResults.Sum(cd => cd.pointsGained);
}
But I'm getting an error saying "Method 'System.Nullable`1[System.Int32] GetUserPoints(Int32)' has no supported translation to SQL."
Any idea how I can get something like this to work?
Regards,
Arseney
I'm sorry for my English. You're code not working because in LINQ to SQL you can not use many contexts. You have many alternative options.
For example concatenating one to one with a subquery.
public List<User> GetLeaderBoard()
{
return (from u in myDB.Users
select new {
User = u,
Sum = (from tc in myDB.TriviaCodes
where tc.userID == u.userID
select c).Sum(p => p == null ? 0 : p.pointsGained)
})
.OrderBy(g => g.Sum)
.Select(g => g.User)
.Take(100)
.Where(u => u.myPoints > 0)
.ToList();
}
Or Use concatenating and grouping
public List<User> GetLeaderBoard()
{
return (from u in myDB.Users
join tc in myDB.TriviaCodes on u.userID equals tc.userID into gj
from subtc in gj.DefaultIfEmpty()
group new { u, subtc } by u into g
select g)
.OrderBy(g => g.Sum(p1 => p1.subtc == null ? 0 : p1.subtc.pointsGained))
.Select(g => g.Key)
.Take(100)
.Where(u => u.myPoints > 0)
.ToList();
}
I used where condition instead of this loop
List<User> users = new List<User>();
foreach (var usr in userResults)
{
if (usr.myPoints > 0)
users.Add(usr);
}

linq after groupby unable to get column values

I am getting data from multiple tables by joining and i want to group data on particular column value but after group by statement i can access my aliases and their properties. What mistake i am making?
public List<PatientHistory> GetPatientHistory(long prid)
{
using(var db = new bc_limsEntities())
{
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = r.resultid,
bookingid = r.bookingid,
testid = r.testid,
prid = r.prid,
attributeid = r.attributeid,
result = r.result,
Test_Name = t.Test_Name,
Attribute_Name = a.Attribute_Name,
enteredon = r.enteredon,
Attribute_Type = a.Attribute_Type
}).ToList();
return result;
}
}
You're doing this wrong way. As been said by Jon after grouping the sequences with aliases r,t,a doesn't exist. After grouping you receive the sequence g with sequances of r,t,a in each element of g. If you want get one object from each group (for example most recent) you should try this:
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = g.Select(x => x.r.resultid).Last(), // if you expect single value get it with Single()
// .... here add the rest properties
Attribute_Type = g.Select(x => x.a.Attribute_Type).Last()
}).ToList();
I appreciated this question so I thought I would add another potential usage case. I would like feedback on what the cleanest approach is to getting table information through a group operation so that I can project later in the select operation. I ended up combining what the OP did which is to pass objects into his group clause and then used the g.Select approach suggested by YD1m to get table information out later. I have a LEFT JOIN so I'm defending against nulls :
// SQL Query
//DECLARE #idCamp as Integer = 1
//
//select *,
//(select
//count(idActivityMaster)
//FROM tbActivityMasters
//WHERE dftidActivityCategory = A.idActivityCategory) as masterCount
//FROM tbactivitycategories A
//WHERE idcamp = #idCamp
//ORDER BY CategoryName
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new {c, m} by new { m.dftidActivityCategory } into g
select new
{
idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
idCamp = g.Select(x => x.c.idCamp).First(),
CategoryName = g.Select(x => x.c.CategoryName).First(),
CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
masterCount = g.Count(x => x.m != null)
}).OrderBy(o=> o.idActivityCategory);
desiredResult.Dump("desiredResult");
If I just use a basic group approach I get the results but not the extra column information. At least I can't find it once I group.
var simpleGroup = (from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
.OrderBy(o => o.CategoryName)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group m by m == null ? 0 : m.dftidActivityCategory into g
select new
{
// How do I best get the extra desired column information from other tables that I had before grouping
// but still have the benefit of the grouping?
// idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
// idCamp = g.Select(x => x.c.idCamp).First(),
// CategoryName = g.Select(x => x.c.CategoryName).First(),
// CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
// masterCount = g.Count(x => x.m != null)
idActivityCategory = g.Key,
masterCount = g.Count(x => x != null)
});
simpleGroup.Dump("simpleGroup");
Please tear this up. I'm trying to learn and it just seems like I'm missing the big picture here. Thanks.
UPDATE : Cleaned up by moving the work into the group and making the select more straight forward. If I had known this yesterday then this would have been my original answer to the OP question.
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new { c, m } by new
{ idActivityCategory = m == null ? 0 : m.dftidActivityCategory,
idCamp = c.idCamp,
CateGoryName = c.CategoryName,
CategoryDescription = c.CategoryDescription
} into g
select new
{
idActivityCategory = g.Key.idActivityCategory,
idCamp = g.Key.idCamp,
CategoryName = g.Key.CateGoryName,
CategoryDescription = g.Key.CategoryDescription,
masterCount = g.Count(x => x.m != null)
}).OrderBy(o => o.idActivityCategory);
desiredResult.Dump("desiredResult");

Convert from query to method

How to convert following code to method operator:
var myOrders = from c in customers
where c.Field<string>("Region") == "WA"
from o in orders
where c.Field<string>("CustomerID") == o.Field<string>("CustomerID")
&& (DateTime)o["OrderDate"] >= cutoffDate
select new {
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
};
---------or----------
var myOrders = from c in customers
where c.Region == "WA"
from o in orders
where c.CustomerID == o.CustomerID
&& o.OrderDate >= cutoffDate
select new {
CustomerID = c.CustomerID,
OrderID = o.OrderID
};
same code in object form
I'd actually rewrite this as a join - probably via intermediate variables:
var washingtonCustomers = customers.Where(c => c.Field<string>("Region") == "WA");
var recentOrders = orders.Where(o => (DateTime)o["OrderDate"] >= cutoffDate);
var query = washingtonCustomers.Join(recentOrders,
c => c.Field<string>("CustomerID"),
o => o.Field<string>("CustomerID"),
(c, o) => new {
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
});
You can try with this code - based on IEnumerable<KeyValuePair<string, Int32>
public IEnumerable<KeyValuePair<string, Int32>> YourQuery(DateTime date, string code)
{
var result =
from c in customers
where c.Field<string>("Region") == code
from o in orders
where c.Field<string>("CustomerID") == o.Field<string>("CustomerID")
&& (DateTime)o["OrderDate"] >= date
select new
{
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
};
return result;
}
Are you just wanting to use the functional Linq syntax instead of query syntax? That would look like:
var myOrders = customers
.Where(c => c.Region == "WA")
.SelectMany(c =>
orders
.Where(o => (o.CustomerID == c.CustomerID)
&& (o.OrderDate > cutoffDate))
.Select(o => new {
CustomerID = c.CustomerID,
OrderID = o.OrderID
})
);

Categories

Resources