LINQ to remove duplicated property - linq

I have a LINQ statement like this:
var media = (from p in postService.GetMedia(postId)
select new
{
PostId = postId,
SynthId = p.SynthId
});
There are many(possibly thousands) of records returned with the same SynthId. I want to select one one, any random one. So when I'm finished, media should contain records with distinct SynthId.
SynthId can be null, I want all nulls to be in media (the distinct should not affect them).
My DAL is EntityFramework, if that will help.
How do I accomplish this in the most efficient way?

Use a grouping query:
var media =
from p in postService.GetMedia(postId)
group p by p.SynthId into g
select g.First();
This will give you the first post in the sequence for each group of records, where the grouping key is the SynthId.
If it's important for you to do a projection (i.e. to use select new { ... }) then you should be able to use the let keyword:
var media =
from p in postService.GetMedia(postId)
group p by p.SynthId into g
let firstPost = g.First()
select new { PostId = firstPost.PostId, SynthId = firstPost.SynthId };
If you want all null values for SynthId to be in their own group, then I would probably filter the first list and then do a concatenation, i.e.:
var media = postService.GetMedia(postId);
var myMedia =
(from p in media
where p.SynthId != null
group p by p.SynthId into g
let firstPost = g.First()
select new { PostId = firstPost.PostId, SynthId = firstPost.SynthId })
.Concat
(from p in media
where p.SynthId == null
select new { PostId = firstPost.PostId, SynthId = firstPost.SynthId });

Related

need to return the first record linq

I need to return only the first record. Although I had to put the z.FirstOrDefault(); still more than one were displayed
public DataTable GetDependents(string EmployeeID)
{
HealthCareSystem.DataClassesDataContext db = new HealthCareSystem.DataClassesDataContext();
var z = (from s in db.SelectingDependentsGroupBies
where s.EmployeeID.Equals(EmployeeID)
join d in db.Dependents on s.DependentID equals d.DependentID
orderby s.DependentID descending
//Selecting wanted tables dependents fields by datatable
select new DependentsX { DependentID = Convert.ToInt32(s.DependentID), EmployeeID = s.EmployeeID, Name = s.Name, Surname = s.Surname, IDCardNo = s.IDCardNo, ContactNo = s.ContactNo, BirthDate = s.BirthDate, StartSchemeDate = s.StartDate, EndSchemeDate = s.EndDate, RelationType = s.Type, Payment = Convert.ToDouble(d.Payment), });
var firstRecord = z.FirstOrDefault();
Add this line after your existing code:
var firstRecord = x.First();
If there might be zero results and you don't want an exception then you can use FirstOrDefault instead.
var firstRecord = x.FirstOrDefault();
Another common way is to enclose your query in parenthesis and then include the extension methods outside the parenthesis:
var x = (from d in db.DependentsRelationsViews
where d.IDCardNo.Equals(DepID)
orderby d.DependentID descending
select new DependentsX
{
DependentID = Convert.ToInt32(d.DependentID),
EmployeeID = d.EmployeeID,
Name = d.Name,
Surname = d.Surname,
IDCardNo = d.IDCardNo,
ContactNo = d.ContactNo,
BirthDate = d.BirthDate,
StartSchemeDate = Convert.ToDateTime(d.StartSchemeDate),
EndSchemeDate = d.EndSchemeDate,
Payment = d.Payment,
RelationType = Convert.ToString(d.Type)
}).FirstOrDefault();
If you don't enclose it in parenthesis and try to call FirstOrDefault() then you're applying that extension to the anonymous type in just the very last Select clause. By enclosing the whole query in parenthesis and then calling the FirstOrDefault() extension, you're indicating to the compiler that you wish to apply it to the return value of your entire query, which will be the IEnumerable<> or IQueryable<> collection.

How can I define a List to add results of a query in a loop?

I have an array filled with long type values and for each value in the array I need to implement a query. I used foreach loop as you can see from the code below:
var result;
foreach(long id in PrdIdArr)
{
var mainQuery = (from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}).Take(_RowNumber);
//var result = mainQuery.ToList();
result.add(mainQuery.ToList());
}
data = this.Json(result);
data.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return data;
However, I have a problem in my code; I have to define a main list just before the foreach loop so that I could add results of each query to the that main list. my question is: How can I define this list as you can see at the beginning of my code? Thanks for the help...
How can I define this list as you can see at the beginning of my code?
Make
new {
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
into a concrete type (say QueryResult, although something a little more specific than that), and then just declare
var result = new List<QueryResult>();
Also, you should consider turning
foreach(long id in PrdIdArr)
and
where s.START_PRD_ID == id
into
where PrdIdArr.Contains(s.Start_PRD_ID)
var result = new List<object>();
foreach(long id in PrdIdArr)
{
....
result.Add(mainQuery.ToList());
}
You could do this:
var result = PrdIdArr.Select(id =>
from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
.Take(_RowNumber)
.ToList()
).ToList();
I highly recommend performing some Extract Method refactorings, as the code is pretty complex and hard to understand/mange this way.
Just create the anonymous type outside with the same property names and the correct type
var result = Enumerable.Range(0, 0).Select(x => new
{
OP_ID = 1,
OP_UPD_DATE = DateTime.Now,
EXTERNAL_ID = 1,
OP_OS_CODE = 1,
OP_START = DateTIme.Now,
OP_ST_STATION = "",
START_PRD_ID = 1,
}).ToList();
And in your loop call AddRange
result.AddRange(mainQuery.ToList());

Linq to Sql Query - better solution (optimizing)

The following code works, but it's not a nice code. (low performance)
I have a dictionary with value and key.
First i go trough every webcodes who exist. Then i load all participants in a list (where webcode equals the actual webcode in the foreach). After that i add the data (parameter of the webcode and a count of participants to the dictionary).
Guid compID = Guid.Parse(wID);
ChartModel webcodes = new ChartModel();
webcodes.Title = "Webcodes Statistics";
webcodes.Data = new Dictionary<string, int>();
var webcodesData = db.t_Webcode;
foreach (var w in webcodesData)
{
var wData = db.t_Participant.Where(t => t.FK_Competition == compID && t.Webcode == w.Webcode);
if (wData.Count() != 0)
webcodes.Data.Add(w.Parameter, wData.Count());
}
ViewBag.Webcodes = webcodes;
TIA
You need something along these lines:
webcodes.Data = (from w in db.t_Webcode
join p in db.t_Participant on w.Webcode equals p.Webcode
where p.FK_Competition == compID
group w by w.Parameter into g
select new { g.Key, Count = g.Count() }).ToDictionary();
I can't test it but that is the type of query you need.
This will assume that you have relationships defined in your database and that your LINQ to SQL datacontext are aware of them. If not, you will need to join manually on t_Participants from tWebcode.
This should execute in 1 single SQL query, instead of 1 query per row in tWebcode.
var webcodesAndNoOfParticipants =
from webcode in db.tWebcode
// Define number of participants for this webcode
let numberOfParticipants = webcode.t_Participants.Count(participant => participant.FK_Competition == compID)
where numberOfParticipants > 0
select new {
WebcodeParameter = webcode.Parameter,
NoOfParticipants = numberOfParticipants
};
webcodes.Data = webcodesAndNoOfParticipants.ToDictionary(x => x.WebcodeParameter, x => x.NoOfParticipants);

Update class field in LINQ - is it possible?

I have 2 tables, and want to get records from 1 table and to "update" one of its fields from another table, and to pass final list of "Payment" objects somewhere. I cannot use anonymouse type, i need to get the list of proper typed objects.
There was a long way.
Got data:
var paymentsToShow = from p in paymentsRepository.Payments
join r in recordTypeRepository.RecordType
on p.RecordType equals r.Reference into p_r
where p.Customer == CustomerRef
from r in p_r.DefaultIfEmpty()
select new
{
Payment = p,
RecordType = r
};
var objList = paymentsToShow.ToList();
Change required field (basically, Payment.RecordTypeName is empty):
foreach (var obj in objList)
{
obj.Payment.RecordTypeName = obj.RecordType.Name;
}
Got list with correct type:
var paymentsList = from o in objList
select o.Payment;
Is there any way to get code shorter, to make required field update in the query or something else? I dont know where to look for.
I cannot change database.
You could do it like this:
var paymentsToShow = (from p in paymentsRepository.Payments
join r in recordTypeRepository.RecordType
on p.RecordType equals r.Reference into p_r
where p.Customer == CustomerRef
from r in p_r.DefaultIfEmpty()
select new
{
Payment = p,
RecordType = r
}).Select(x =>
{
x.Payment.RecordTypeName = x.RecordType.Name;
return x.Payment;
});
This will result in an IEnumerable<Payment>, so no anonymous type used.

Linq extract a count() value from a data object

I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });

Resources