Linq select one occurrence - c#

how can I use link to fetch one-to-one relation that does not contain duplicates? Example:
ID | STATUS
1 | CHECKIN
2 | CHECKOUT
2 | CHECKOUT
1 | CHECKIN
3 | CHECKOUT <--
I should only retrieve the ID 3 CHECKOUT because he is not duplicated.
Can you help me using linq?

You need to make a Group and ask for only group items that = 1
Dim nonDuplicates = (From x In query Group By x.Id, x.Status Into grp = Group
Where grp.Count = 1)

The other answer will still retrieve all the duplicated items, just removing duplicates of them. If you want to only retrieve non-duplicated items, as you stated in your original question, this will work for you:
Item singles = items.Where(i => !items.Any(j => !i.Equals(j) && i.id == j.id));

Related

Entity Framework - Query to obtain last 2 unique entries of a column and then their relevant data

I am new to entity framework and I am having difficulty coming up with a query. For the following situation.Say I have the following model
class Student
{
[Key]
public int Id { get; set; }
public string name{get;set;}
public string guid{get;set;}
}
and my table looks like this
id| name | guid
--------------
1 Andrew | C
2 John | D
3 Adam | B
4 Charles| A
5 Jacob | A
Now I would like to get the last two unique GUIDs which will be A and B.
and then I would like to get all row in which these GUIS appeared in the table. so I would like Jacob,Charles and Adam returned. Any suggestions on how I can get started with this ? I know Ill have to sortby then select unique. But I am not sure how Ill do it in Entity.
I think you could do something like this:
var result = dbcontext.Students.OrderByDescending(s => s.Id)
.GroupBy(s => s.Guid)
.Take(2)
.SelectMany(s => s.Take(1));
1- Order by Id descending to get to the last Id's first.
2- Then group by the Guid and take the first 2 groups.
3- Select the top 1 for each group.
You need to only use this if you want output as Jacob,Charles and Adam
Original Credits #Stackberg use suggested query like without Take if the indented result.
//This work against in memory list (TESTED)
var result = dbcontext.Students.OrderByDescending(s => s.Id)
.GroupBy(s => s.Guid)
.Take(2)
.SelectMany(s=>s.ToList());
//IF EF doesn't able to convert the query you can try this.
var inMemoryGrouped = dbcontext.Students.OrderByDescending(s => s.Id)
.GroupBy(s => s.Guid)
.Take(2)
.ToList();
//Flatten the items using select many
var final = inMemoryGrouped.SelectMany(y=>y.ToList());
Only thing it differ from #Stackberg answer is omitted the Take after grouping.

How to apply self join in Linq Query?

Books Table
Id VendorId ASIN Price
-- -------- ---- ------
1 gold123 123 10
2 sil123 123 11
3 gold456 456 15
4 gold678 678 12
5 sil456 456 12
6 gold980 980 12
I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.
Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.
I tried following
var gold = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("gold"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
var asinsForGold = gold.Select(x => x.ASIN).ToList();
var correspondingSilver = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("sil")
&& asinsForGold.Contains(x.ASIN)).ToList();
var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();
var goldWithoutCorrespondingSilver = gold.Where(x =>
!correspondingSilverAsins.Contains(x.ASIN));
Can We apply self join or better way to get result only in one query instead of two query and several other list statement.
It's just another predicate, "where a corresponding silver vendor doesn't exist":
var goldWoSilver = _repository.Query<Books>()
.Where(x => x.VendorId.Contains("gold"))
.Where(x => !_repository.Query<Books>()
.Any(s => s.ASIN == x.ASIN
&& s.VendorId.Contains("sil"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.
See if it helps -
var goldWithoutCorrespondingSilver = from b1 in books
join b2 in books on b1.ASIN equals b2.ASIN
where b1.VendorId.Contains("gold")
group b2 by b1.VendorId into g
where !g.Any(x => x.VendorId.Contains("sil"))
select g.FirstOrDefault();
What I have done is -
Selected records with matching ASIN
Grouped them by VendorID
Selected ones which do not have sil

How do I use Group By and MAX in Linq to return multiple rows?

Clarified example
I have a database of users that is created by a script that scans through Active Directory. One of the fields it applies is a "ScanDate" field, which indicates when the scan took place. The script scans through multiple Active Directory domains.
GOAL: Obtain an IList from the database that contains the list of users for ALL domains, but where the ScanDate is the MAX(ScanDate) for each domain.
This ensures I get the freshest data for each domain.
A SQL query that appears to work for me:
SELECT *
FROM ADScans a
WHERE a.ScanDate = (SELECT MAX(b.ScanDate) FROM ADScans b WHERE a.Domain = b.Domain) AND Enabled = 1
However, having trouble getting that expressed in LINQ
e.g.:
Category | Date
Cat1 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Cat1 | 4/3/16
I would expect a list:
Cat1 | 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Some clarification
I would expect to have multiple rows returned per category - the MAX(Date) will not just give me one. I am looking to obtain ALL of the rows for the MAX(Date) of each category.
Something like this should work:
var result =
list
//Group by Category
.GroupBy(x => x.Category)
//For each Category, select Category and max Date within the Category
//This would create an anonymous object, you could do a "new Entity" instead if you want
.Select(g => new {Category = g.Key, Date = g.Max(x => x.Date)})
.ToList();
I'm an idiot.
from u in this.db.ADScans
where u.ScanDate ==
(from s in this.db.ADScans where u.Domain == s.Domain select s.ScanDate).Max()
&& u.Enabled
select u;
Rather than using Max(), just order the items in the groups and take the top item: since you ordered the items, it's guaranteed to be the highest one:
var mostRecentScanFromEachDomain =
from u in this.db.ADScans
where u.Enabled
group u by u.Domain into g
select g.OrderByDescending(u => u.ScanDate)
.FirstOrDefault();
You can GroupBy by Domain to get the max ScanDate, then keep only the rows with that Date.
For a model like this:
class ADScan
{
public int Domain { get; set; }
public DateTime ScanDate { get; set; }
}
You can get the scans doing this:
var result = scans.GroupBy(s => s.Domain)
.SelectMany(g => g.Where(s => s.ScanDate == g.Max(d => d.ScanDate)));
This produces a collection containing the scans with the max ScanDate for its Domain.

LINQ Sum of entries based on latest date

I have a table like so:
Code | BuildDate | BuildQuantity
---------------------------------
1 | 2013-04-10 | 4
1 | 2014-09-23 | 1
1 | 2014-08-20 | 2
7 | 2014-02-05 | 4
I want the LINQ query to pick up the LATEST Build date for each Code, pick the BuildQuantity for that Code, and so on for all the records, and sum it up. So for the data above, the result should be 1 + 4 = 5.
This is what I'm trying:
var built = (from tb in db.Builds
orderby tb.BuildDate descending
group tb by tb.Code into tbgrp
select tbgrp.Sum(c => c.BuildQuantity)).First();
This query returns 7... What am I doing wrong?
You are summing all code's BuildQuantities before you take the first, instead you want to sum the firsts.
int built = db.Builds
.GroupBy(b => b.Code)
.Sum(g => g.OrderByDescending(b => b.BuildDate).First().BuildQuantity);
You are looking for the sum of the build quantity of the last entry per code. You're currently ordering before you group, which doesn't actually do anything (after the group, the ordering isn't defined)
So first of, you're looking to get the latest element by code. Lets group first. I'm more comfortable writing through the extension methods:
IGrouping<int, Build> grouped = db.Builds.GroupBy(tb => tb.Code)
we now have the elements grouped. From each group, we want to get the first element ordered descending by build date.
var firsts = grouped.Select(gr => gr.OrderByDescending(gr => gr.BuildDate)
.First())
finally, we can get the sum:
var sum = firsts.Sum(tb => tb.BuildQuantity);
plugging this all together becomes
var sum = db.Builds.GroupBy(tb => tb.Code).
.Select(gr => gr.OrderByDescending(gr => gr.BuildDate).First())
.Sum(tb => tb.BuildQuantity);
Group by has overloads that allows you to roll almost everything in the group.
If you like compact code, you could write
var sum = db.Builds
.GroupBy(tb => tb.Code,
tb => tb,
gr => gr.OrderByDescending(gr => gr.BuildDate)
.First()
.BuildQuantity)
.Sum()
though I wouldn't recommend it from a readability point of view

NHibernate - LINQ Query Many to Many Issue

I'm trying to upgrade an existing application to use NHibernate. My database has the following tables:
Sites:
- Id (PK)
- Name
Categories:
- Id (PK)
- Name
CategoriesSite
- CategoryId (PK)
- SiteId (PK)
- Active
For each category and site a record may or may not exist in the CategoriesSite table. If an item exists in the CategoriesSite table then it can turn the Category off by setting Active to false. If it doesn't then it assumes Active is true.
I'd like to create a LINQ query in NHibernate to filter for categories of a particular site (that are active). For example say I have the following data:
Sites:
Id | Name
1 | Site 1
2 | Site 2
Categories:
Id | Name
1 | Category 1
2 | Category 2
CategoriesSite:
CategoryId | SiteId | Active
1 | 1 | True
1 | 2 | True
2 | 1 | False
I could say:
var categories = session.Query<CategorySite>()
.Where(s => s.Site.Id == 2 && s.Active)
.Select(s => s.Category)
.ToList();
However this will only get Category 1 and not Category 2 which I'd like it to do. I was wondering if anyone has done anything similar and could suggest either a way to query this or offer any recommendations on how I can map this scenario better.
Without seeing the generated query, I can only guess but, try this instead:
var categories = session.Query<CategorySite>()
.Where(s => s.SiteId == 2 && s.Active) // not s.Site.Id
.Select(s => s.Category)
.ToList();
I think i've solved this. I added a one to many collection against the Category for the list of Categorory Sites. This allows me to say:
var categories = session.Query<Category>()
.Where(c => !c.Sites.Any(s => s.Site.Id == 2 && !s.Active))
.ToList();
This means it will only not return the category when it has been set in-active and will still return the Category when no record exists in the CategoriesSite table.

Categories

Resources