How to get unique value with LINQ>SQL? - c#

Using LINQ to SQL, how do I get the row with 1, 21? I'm looking for
SomeId==1
and
SecondId is a unique entry
SomeId SecondId
0 20
1 21
1 22
1 22
EDIT:
Ok, sorry. That wasn't clear. What I'm trying to do is generically find that row. There might be another entry that looks like this:
1 25
And that is the only 25. So I would get back two rows. Without referencing specific Ids, how do I find these two rows?

EDIT: Okay, it was really unclear what you meant before, but now I think I see what you mean, and you want something like:
var query = from row in table
where row.SomeId == targetId
group row by row.SecondId into g
where g.Count() == 1
select g.Single();
In other words:
Filter by SomeId first
Group by SecondId
Filter so that only groups with a single entry for that SecondId are returned
Select the sole entry from that group
There can be multiple such groups, of course - so you would get (1, 21) and (1, 25) in your example.

EDIT: If you saying you would like to find any combination of SomeId & SecondId where there are more than one row for that combination? then you could do the following:
var results = source.Where(x => x.SomeId == 1).GroupBy(x => x.SecondId).Where(g => g.Count > 1);
This will give you groups of results, and only return those that have more than one row. So in your example, you would get a group that returns 1,22...
If you are looking for the case where you only have rows in which there is a single entry in the table with that combination (the opposite of what I'm returning) you can change the comparison operator from '>' to '==' and another answer-er has also shown this possibility.

Related

Get list of items where their ID is equal to some Values - linq c#

Here is a query:
from order in db.tblCustomerBuys
where selectedProducts.Contains(order.ProductID)
select order.CustomerID;
selectedProducts is a list containing some target products IDs, for example it is { 1, 2, 3}.
The query above will return customerIDs where they have bought one of the selectedProducts. for example if someone has bought product 1 or 2, its ID will be in result.
But I need to collect CustomerIDs where they have bought all of the products. for example if someone has bought product 1 AND 2 AND 3 then it will be in result.
How to edit this query?
the tblCustomerBuys are like this:
CustomerID - ID of Customer
ProductID - the product which the customer has bought
something like this:
CustomerID ProdcutID
---------------------------
110 1
110 2
112 3
112 3
115 5
Updated:
due to answers I should do grouping, for some reason I should use this type of query:
var ID = from order in db.tblCustomerBuys
group order by order.CustomerID into g
where (selectedProducts.All(selProdID => g.Select(order => order.ProductID).Contains(selProdID)))
select g.Key;
but it will give this error:
Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
The updated query is the general LINQ solution of the issue.
But since your query provider does not support mixing the in memory sequences with database tables inside the query (other than Contains which is translated to SQL IN (value_list)), you need an alternative equivalent approach of All method, which could be to count the (distinct) matches and compare to the selected items count.
If the { CustomerID, ProductID } combination is unique in tblCustomerBuys, then the query could be as follows:
var selectedCount = selectedProducts.Distinct().Count();
var customerIDs =
from order in db.tblCustomerBuys
group order by order.CustomerID into customerOrders
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Count() == selectedCount
select customerOrders.Key;
And if it's not unique, use the following criteria:
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Select(order => order.ProductID).Distinct().Count() == selectedCount
As your question is written, it is a bit difficult to understand your structure. If I have understood correctly, you have an enumerable selectedProducts, which contains several Ids. You also have an enumeration of order objects, which have two properties we care about, ProductId and CustomerId, which are integers.
In this case, this should do the job:
ver result = db.tblCustomerBuys.GroupBy(order => order.CustomerId)
.Where(group => !selectedProducts.Except(group).Any())
.Select(group => group.Key);
What we are doing here is we are grouping all the customers together by their CustomerId, so that we can treat each customer as a single value. Then we are treating group as a superset of selectedProducts, and using a a piece of linq trickery commonly used to check if one enumeration is a subset of another. We filter db.tblCustomerBuys based on that, and then select the CustomerId of each order that matches.
You can use Any condition of Linq.
Step 1 : Create list of int where all required product id is stored
Step 2: Use Any condition of linq to compare from that list
List<int> selectedProducts = new List<int>() { 1,2 } // This list will contain required product ID
db.tblCustomerBuys.where(o=> selectedProducts .Any(p => p == o.ProductID)).select (o=>o.order.CustomerID); // This will return all customerid who bought productID 1 or 2

mvc Groupby and Orderby nested

mvc beginner
I have a table of lots that contain a property Num_of_steps representing the number of completed steps toward building a house.
I currently use this to retrieve the lot information and am sorting by the lot number.
var ViewModel = new Sub_lot_VM();
ViewModel.Subdivisions = db.Subdivisions
.Include(i => i.Lots)
.ToList();
if (ViewModel.Subdivisions !=null) // if data sort by lot number
{
foreach (var item in ViewModel.Subdivisions)
item.Lots = item.Lots.OrderBy(i => i.LotName).ToList();
}
return View(ViewModel);
}
Now I want to display this information a 3 groups:
first where the count is between 1 and 114 (active),
second where the count is above 115 (or GTE 115?) (finished)( and then orderby lot name) and
third group is count = 0 (not started) also order by lotname.
I've been trying to think of how to add .where and .groupby lambda expressions to my method without luck. Such as.where(I=>i.Lot.Num_of_steps=0).
I also see that I needed a foreach where some LINQ examples did not need the foreach. Still confused on that.
Get the lots first and then use groupby with ranges to get the groups
from x in
(
db.Subdivisions.SelectMany(sd => sd.Lots)
)
group x by x.Num_of_steps == 0 ? 3 : x.Num_of_steps < 115 ? 1 : 2 into g
orderby g.Key
select g.OrderBy(g1 => g1.LotName)
You can give the groups meaningful names in stead of 1, 2 and 3, but you can also postpone that until it's display time. The numbers facilitate correct sorting.

How can I use sum with group by in LINQ?

I have the following many companies. Some of the companies have subjects and others do not. Something like this:
CompanyID Subjects
1 2
2 4
3 1
4 0
I am trying to create a LINQ report that will give me this information. This is what I have so far. It correctly does an outer join so that even companies with no subjects are include in the list. Once I have that data then I group the date by company title. The problem is that the last select does not work correctly. Can someone suggest how I can get the sum. I was able to use count() but I need a sum as the way I have set things up is that when there are no subjects a value of 0 goes into Subjects and where there is 1 a value of one goes there. So by summing the count of Subjects at each break in the group I should be able to find out how many subjects are assigned to the company.
var test1 = from c in companies
join s in subjects
on "0000" + c.RowKey equals s.PartitionKey into outer
from s in outer.DefaultIfEmpty()
select new
{
Title = c.Title,
Subjects = ((s == null) ? 0 : 1)
} into split
group split by split.Title into g
select new
{
Title = g.Key,
total = g.sum(s => s.Subjects)
};
You wrote the "sum" method without the capital "s". C# is case sensitive, so I think that's your problem, if you're getting a compile error.
Otherwise include the actual result of your query and what you expect, so we can compare it and try to find the problem.

linq skip count when count is zero

I'm doing a linq-to-sql query and I wish the LastOrDefault operator were available but it's not. So, I'm writing the query like this:
TheUserNote = ((from note in MyDC.UserNotes
where note.UserID == TheUserID
select note.NoteText).Skip(
(from n in MyDC.UserNotes
where n.UserID == TheUserID
select n.NoteID).Count() - 1)).SingleOrDefault(),
Basically, I want to use Skip and Count to get to the last item: count how many items there are, substract 1, and skip for that number.
It's not working and I'm looking to fix it. The problem is that sometimes Count can be 0 so I get an error saying parameters are not valid since in that case Count will be -1.
Any suggestions?
Thanks.
You might try .Reverse().FirstOrDefault(), or if you have a date column or primary key column try .OrderByDescending(...).FirstOrDefault().
Using your variable names and comment:
var TheUserNote
= MyDC
.UserNotes
.Where(x => x.UserId == TheUserID)
.OrderByDescending(x => x.NoteDateTime)
.FirstOrDefault()
;
Is there a specific order they are coming back in? I assume so otherwise why would it matter if it was the last or the first one you got?
Can't you just order by whatever in the opposite direction and use FirstOrDefault?

Return Top X Results From Linq To Objects Query in Order

PREVIOUSLY: In this question someone told me how to use CompareTo to return surnames within a particular range ordered alphabetically using LINQ to Objects.
The rest of my question, which seems to have missed the initial question asking feeding frenzy, actually arose after I tested this solution. In the previous eaxample I had a list of names:
Adams
Bentham
Bickford
Gillies
Kelly
Moore
Peters
Rutherford
Smith
Taylor
Williams
And I wanted to be able to query them for all the names between Gillies and Moore for example and get:
Gillies
Kelly
Moore
This is all well and good if you want every single solitary name inbetween the goalposts returned no matter what. The problem comes in when you have a huge quantity of surnames and you want a maximum of four of the names between Gillies and Taylor returned in alphabetical order, for example.
So the desired output is:
Gillies
Kelly
Moore
Peters
However just returning four results between Gillies and Taylor could return Kelly, Peters, Smith and Taylor, or Gillies, Moore, Rutherford and Smith. Basically, the query takes you at your word and just selects any old four between the goalposts.
So, how can I get the top 4 results alphabetically. I could write a second query of course and return a subset and then select from within that... but shouldn't there be a way to integrate this behaviour into the initial query?
I've tried a couple of things with OrderBy and so far they just don't work. So it's over to you guys.
EDIT: for those two of you who've suggested using "take" I already am using take. It doesn't "take" in order, even if you use OrderBy or at least it doesn't in my query. Here it is:
var allIDs = (from cust in dc.orders
join item in dc.order_items on cust.orderid equals item.orderid
join del in dc.deliveries on cust.deliveryid equals del.deliveryid
join dt in dc.deliverytypes on del.deliverytype equals dt.deliverytypeid
where eventcode == item.itemcode
&& dt.description == "Secure Post"
&& (cust.status == "OK" || cust.status == "PB")
&& cust.surname.CompareTo(surfrom ?? " ") >= 0
&& cust.surname.CompareTo(surto ?? "zzz") <= 0
&& (cust.trackingcode == null ? false : (bool)cust.trackingcode)==false
orderby cust.surname, cust.initials, cust.ordercode
select cust.orderid).Distinct().Take(ordermax);
That just returns four names from between the names you've selected, not a specific four names.
From your edit it looks like you're doing the orderby and distinct in a strange order:
This works for me (where "allMyNames" is just a List<string>).
var ofInterest = allMyNames
.Distinct()
.Where(x => x.CompareTo(from) >= 0 && x.CompareTo(to) <= 0)
.OrderBy(x => x)
.Take(4);
I'm much happier using the extension form of LINQ :)
Use the "Take" LINQ method to take the first 4 records:
var query = (from name in originalList
where name.CompareTo(fromName) >= 0 && name.CompareTo(toName) <= 0
orderby name
select name).Take(4);
GetNames(...).Take(4);
Will take the first four items in the enumerable.

Categories

Resources