C# Linq query condition - c#

I have a table
I want to grab only the customer where the status = 1 for all rows. so in this example it would only grab John since Bob has status=2 for one record.
I would like to do this in linq c#. Do I need to group it first for all customers? and then have another query to check if it doesn't contain <> 2 then print it?
what would be the right way?

you were right on. Given:
class Customer
{
public string Name { get; set; }
public int Status { get; set; }
}
you'll want:
var names = customers.GroupBy(c => c.Name)
.Where(c => c.All(cc => cc.Status == 1))
.Select(c => c.Key)
.ToList();
Group By Name
Grab items where all Status = 1
Select the Names of the result

var data=customers.where(c => c.status!=2).select(c=>c.Customer).Tolist().distnict();

Related

Dictionary problems: results.Read<NotificationRule>().ToDictionary(rule => rule.Id) states duplicate key

I have a slight problem I don't seem to understand.
I have this bit of code:
SELECT sr.RuleId, s.Id, s.Name FROM Sites s
INNER JOIN NotificationSiteRules sr ON s.Id = sr.SiteID
WHERE sr.RuleId IN (
SELECT r.Id FROM NotificationRules r
INNER JOIN NotificationSiteRules sr ON r.Id = sr.RuleId
WHERE r.IsDeleted = 0 AND (#siteId IS NULL OR sr.SiteId = #siteId)
)
which returns the following set:
1 1 SiteOne
3 1 SiteOne
7 1 SiteOne
1 5 SiteTwo
As you can see, for rule 1 I have both SiteOne and SiteTwo. This must be permitted.
The definition of NotificationRule object is:
public class NotificationRule
{
public NotificationRule()
{
Sites = new List<Site>();
Recipients = new List<Recipient>();
}
public int? Id { get; set; }
public string Name { get; set; }
public List<Site> Sites { get; set; }
public List<Recipient> Recipients { get; set; }
}
So in this definition, it's actually stated that by each Id I should be able to have a list of Sites... But I am getting
System.ArgumentException: An item with the same key has already been added.
when I do
var rules = results.Read<NotificationRule>().ToDictionary(rule => rule.Id);
What am I doing wrong?
Sorry, I am editing the question because I am afraid I was not clear as to what I am trying to achieve.
The final result I was hoping for is of this form:
{1, [1,5],[SiteOne, SiteTwo]}
Which would correspond to:
{Key, List<Recipient>, list<Site>}
As you can see, in this construct I wouldn't have two keys, because all ends up into the same element.
As long as objects in the collection results have not unique values in the field Id you have to group results before put them to the dictionary.
results.Read<NotificationRule>()
.GroupBy(rule=>rule.Id).ToDictionary(group => group.Key, group=>group.ToArray());
It sounds like you may be wanting to do a group by rather than creating a dictionary.
var rules = results.Read<NotificationRule>().GroupBy(k => rule.Id);
This will group the rules by rule.Id
or if you are just trying to get the sites for a specific rule you could do
var siteId = 1;
var sites = results.Read<NotificationRule>().Where(r => r.Id == siteId);
Actually I just found that the problem is not in that little bit of code; that one is handled ok. The problem is that #siteId is arriving null, and then I get duplicates in a previous query.
I am closing this question as the point is now moot; I need to figure out a way to fix the previous query to get me the correct value... or a way to handle that null.
Thank you all for your help!

Random with condition

I have the following code to extract records from a dbcontext randomly using Guid class:
var CategoryList = {1,5};
var generatedQues = new List<Question>();
//Algorithm 1 :)
if (ColNum > 0)
{
generatedQues = db.Questions
.Where(q => CategoryList.Contains(q.CategoryId))
.OrderBy(q => Guid.NewGuid()).Take(ColNum).ToList();
}
First, I have a list of CategoryId stored in CategoryList as a condition to be fulfilled when getting records from the db. However, I would like to achieve an even distribution among the questions based on the CategoryId.
For example:
If the ColNum is 10, and the CategoryId obtained are {1,5}, I would like to achieve by getting 5 records that are from CategoryId = 1 and another set of 5 records from CategoryId = 5. If the ColNum is an odd number like 11, I would also like to achieve an even distribution as much as possible like maybe getting 5 records from CategoryId 1 and 6 records from CategoryId 2.
How do I do this?
This is a two step process,
Determine how many you want for each category
Select that many items from each category in a random order
For the first part, define a class to represent the category and how many items are required
public class CategoryLookup
{
public CategoryLookup(int catId)
{
this.CategoryId = catId;
}
public int CategoryId
{
get; private set;
}
public int RequiredAmount
{
get; private set;
}
public void Increment()
{
this.RequiredAmount++;
}
}
And then, given your inputs of the required categories and the total number of items required, work out how many are required for each category
var categoryList = new []{1,5};
var colNum = 7;
var categoryLookup = categoryList.Select(x => new CategoryLookup(x)).ToArray();
for(var i = 0;i<colNum;i++){
categoryLookup[i%categoryList.Length].Increment();
}
The second part is really easy, just use a SelectMany to get the list of questions (Ive used a straight linq to objects to test, should work fine for database query. questions in my code would just be db.Questions in yours)
var result = categoryLookup.SelectMany(
c => questions.Where(q => q.CategoryId == c.CategoryId)
.OrderBy(x => Guid.NewGuid())
.Take(c.RequiredAmount)
);
Live example: http://rextester.com/RHF33878
You could try something like this:
var CategoryList = {1,5};
var generatedQues = new List<Question>();
//Algorithm 1 :)
if (ColNum > 0 && CategoryList.Count > 0)
{
var take = // Calculate how many of each
// First category
var query = db.Questions
.Where(q => q.CategoryId == CategoryList[0])
.OrderBy(q => Guid.NewGuid()).Take(take);
// For all remaining categories
for(int i = 1; i < CategoryList.Count; i++)
{
// Calculate how many you want
take = // Calculate how many of each
// Union the questions for that category to query
query = query.Union(
query
.Where(q => q.CategoryId == CategoryList[i])
.OrderBy(q => Guid.NewGuid()).Take(take));
}
// Randomize again and execute query
generatedQues = query.OrderBy(q => Guid.NewGuid()).ToList()
}
The idea is to just get a random list for each category and add them all together. Then you randomize that again and create your list. I do not know if it will do all this on the database or in memory, but it should be database I think. The resulting SQL will look horrible though.

Get position of a specific id in an order by query

I need to do a query in c# to get the position of a specific id, in a table order by a date.
My table structure
IdAirport bigint
IdUser int
AddedDate datetime
Data:
2 5126 2014-10-23 14:54:32.677
2 5127 2014-10-23 14:55:32.677
1 5128 2014-10-23 14:56:32.677
2 5129 2014-10-23 14:57:32.677
For example, i need to know in which position is the IdUser=5129, in the IdAirport=2, order by AddedDate asc. (The result in this case will be 3).
Edit:
im using iQueryables like this:
AirPort airport = (for airport as context.Airport select airport).FirstOrDefault();
Thanks for your time!
Using LINQ: If you want to find the index of an element within an arbitrary order you can use OrderBy(), TakeWhile() and Count().
db.records.Where(x => x.IdAirport == airportId)
.OrderBy(x => x.AddedDate)
.TakeWhile(x => x.IdUser != userId)
.Count() + 1;
Here's a quick one :
public class test
{
public int IdAirport;
public int IdUser;
public DateTime AddedDate;
public test(int IdAirport, int IdUser, DateTime AddedDate)
{
this.IdAirport = IdAirport;
this.IdUser = IdUser;
this.AddedDate = AddedDate;
}
}
void Main()
{
List<test> tests = new List<test>()
{
new test(2, 5126, DateTime.Parse("2014-10-23 14:54:32.677")),
new test(2, 5127, DateTime.Parse("2014-10-23 14:55:32.677")),
new test(1 , 5128 , DateTime.Parse("2014-10-23 14:56:32.677")),
new test(2 , 5129 , DateTime.Parse("2014-10-23 14:57:32.677"))
};
var r = tests
.Where(t => t.IdAirport == 2)
.OrderBy(t => t.AddedDate)
.TakeWhile(t => t.IdUser != 5129)
.Count() + 1;
Console.WriteLine(r);
}
It keeps the exact order of your own list. You can modify Where/OrderBy if you wish, the interesting part is in the "TakeWhile/Count" use.
Should work fine but probably not very efficient for long lists.
edit : seems to be the same as Ian Mercer. But the "+ 1" in my own sample is needed since TakeWhile will return the number of skipped items, hence not the position of the good one. Or I didn't get well the issue.
This should do what you need:
dataTable.Rows.IndexOf(
dataTable.AsEnumerable().OrderBy(
x => x["AddedDateColumn"]).First(
x => (int)(x["IdUserColumn"]) == 5129));

LINQ - Simulating multiple columns in IN clausule

In oracle I can do the following query:
SELECT *
FROM Tabl Tabb
WHERE (tabb.Col1, tabb.Col2) IN ( (1,2), (3,4))
Consider I 've following entity:
public class Tabb
{
public int Col1 {get; set; }
public int Col2 {get; set; }
// other props
}
and criteria class
public class Search
{
public int Col1 {get; set; }
public int Col2 {get; set; }
}
I need to write:
public IEnumerable<Tabb> Select(IEnumerable<Search> s)
{
var queryable = this.context.Tabbs;
return queryable.Where(\* some *\).ToList();
}
How can I select entities, that search collection contain instance of search that has the same value of Col1 and Col2?
EDIT:
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
It doesn't work (As I expected) - in may case entity2 is not a entity table, it is static collection, so EF throws exception (sth like: cannot find mapping layer to type Search[]);
There's a few ways, which all have pros and cons, and are sometimes a little bit tricky...
Solution 1
You enumerate the ef part first (of course, depending on the size of your data, this might be a very bad idea)
Solution 2
You concatenate your fields with an element you're sure (hum) you won't find in your fields, and use a Contains on concatenated EF data.
var joinedCollection =entity2.Select(m => m.field1 + "~" + m.field2);
var result = entity.Where(m => joinedCollection.Contains(m.field1 + "~" + m.field2));
of course, this would be a little bit more complicated if field1 and field2 are not string, you'll have to use something like that
SqlFunctions.StringConvert((double)m.field1) + "~" + //etc.
Solution 3
you do this in two step, assuming you will have "not too much result" with a partial match (on only one field)
var field1Collection = joinedCollection.Select(m => m.field1);
var result = entity.Where(m => joinedCollection.Contains(m.field1)).ToList();
then you make the "complete join" on the two enumerated lists...
Solution 4
use a stored procedure / generated raw sql...
Just understood the problem better. You want all rows where the columns match, may be this will help:
myDBTable.Where(x =>
myStaticCollection.Any(y => y.Col2 == x.Col2) &&
myStaticCollection.Any(y => y.Col1 == x.Col1))
.ToList()
.Select(x => new Search { Col1 = x.Col1, Col2 = x.Col2 });
This is saying, I want each row where any Col2 in my static collection matches this database Col2 AND where any Col1 matches this database Col1
this.context.Searches.Join(
this.context.Tabbs,
s => s.Col2,
t => t.Col2,
(search, tab) => new {
search,
tab
});
This will bring back IEnumerable<'a> containing a search and a tab
This guy is doing something similar LINK
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
Once you have your result then you want to enumerate that to make sure you're hitting the database and getting all your values back. Once they're in memory, then you can project them into objects.
result.ToList().Select(a => new MyEntity { MyProperty = a.Property });

Sorting a list based on another attribute in another list

I have the objects as below:
public class CustomerSequence
{
public string CustomerName { get; set; }
public int Sequence { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Component { get; set; }
}
Let's say I have 2 Lists Object
Customer: CustomerSequence:
Id Name Component CustomerName Sequence
1 AAA AAAAAA AAA 2
2 BBB BBBBBBB BBB 4
3 CCC CCCCC CCC 1
DDD 3
As you can see there is no DDD in List.
I want to sort the List Customer based on the List CustomerSequence
Result is:
Customer:
Id Name Component
3 CCC CCCCC
1 AAA AAAAAA
2 BBB BBBBBBB
Anyone can help me please.
Join both sequences on customer name, then order by sequence value:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName
orderby cs.Sequence
select c;
Lambda syntax is not that beautiful, and it will look like
customers.Join(customerSequences,
c => c.Name, cs => cs.CustomerName, (c,cs) => new { c, cs })
.OrderBy(x => x.cs.Sequence)
.Select(x => x.c)
Internally join uses lookup for second sequence, which is much more effective then linear search with Where.
If it is possible that there is no CustomerSequencs matching customer, or there is more than one match, then use group join:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName into g
orderby g.Select(cs => cs.Sequence).FirstOrDefault()
select c
This query uses 0 form missing sequences, and first matched value if there is more than one sequence for customer.
Try this
Customer.OrderBy(x => CustomerSequence.Where(y => y.CustomerName == x.Name)
.Select(y => y.Sequence)
.FirstOrDefault())
Alternatively you can use a join which would be better if the source was a database
var sorted =
from c in customer
join csj in customerSequence on c.Name equals csj.CustomerName into customerSequenceJoined
from cs in customerSequenceJoined.DefaultIfEmpty()
orderby cs == null ? 0 : cs.Sequence
select c;
The cs == null ? 0 : cs.Sequence deals with the case when there is no matching record in the sequence collection. You could use int.MaxValue if you want these items to appear last.
Use Join
var customers = from cust in Customer
join cust_seq in CustomerSequence
on cust.Name equals cust_seq.CustomerName
orderby cust_seq.Sequence
select cust;
I tend to use a dictionary for this sort of thing.
var customerSequence =
customerSequences
.ToDictionary(x => x.CustomerName, x => x.Sequence);
var sortedCustomers =
customers
.OrderBy(x => customerSequence[x.Name])
.ToList();

Categories

Resources