How to pick a specific object from a Linq Query using Distinct? - c#

I have a list with two or more objects of class Agent.
Name = "A"
Priority = 0
ResultCount = 100
;
Name = "B"
Priority = 1
ResultCount = 100
;
Both objects have the same ResultCount. In that case I only need one object and not two or more. I did this with a Linq Query with Distinct and an custom made Comparer.
IEnumerable<Agent> distinctResultsAgents =
(from agt in distinctUrlsAgents select agt).Distinct(comparerResultsCount);
With this query I get only one object from the list but I never know which one.
But I don't want just any object, I want object "B" because the Priority is higher then object "A".
How can I do that?
My custom Comparer is very simple and has a method like this:
public bool Equals(Agent x, Agent y)
{
if (x == null || y == null)
return false;
if (x.ResultCount == y.ResultCount)
return true;
return false;
}

First group the elements by ResultCount so that you only get one result for each distinct value of ResultCount. Then for each group select the element in that group with the highest priority.
Try this query:
IEnumerable<Agent> distinctResultsAgents =
from d in distinctUrlsAgents
group d by d.ResultCount into g
select g.OrderByDescending(x => x.Priority).First();
If you use morelinq there is a function called MaxBy that you could use instead of the last line, but note that it only works for LINQ To Objects.

Related

LINQ sentence, check if remain elements

We're new in LINQ and we want to know if the are any method to check if there are elements remaining in the sentence like the ResultSet.next() from Java.
In Java the resultSet return true if there are elements remaining and false if not. We want to know if the is a method like that in LINQ.
public List<Product> FindProductsByKeyword(string productName, Category category, int page, int size)
{
DbSet<Product> products = Context.Set<Product>();
List<Product> result;
if (category == null)
{
result = (from p in products
where p.productName.Contains(productName) //, StringComparison.OrdinalIgnoreCase)
orderby p.productName descending
select p).Skip(page).Take(size).Include("Category").ToList();
}
else
{
result = (from p in products
where p.productName.Contains(productName)
&& p.categoryId == category.id
orderby p.productName descending
select p).Skip(page).Take(size).ToList();
}
return result;
}
This our code( I dont know if this will help) we take elements with a number of Size but we dont know if there are more elements.
Using the terminating method ToList() initiates a DB query and parses the returned table as a simple in-memory List<T> (same as ArrayList<T> in Java).
If you want to check if there are any entries in the result, you can just check the list's Count property:
if (result.Count > 0)
{
// Do something
}
Or use LINQ's Any() extension method, which is a bit more readable in this context:
if (result.Any())
{
// Do something
}
If you returned an Enumerable, then you could use Enumerable.MoveNext()
For example:
var p = products.AsEnumerable();
var i = p.GetEnumerator();
while( i.MoveNext())
i.Current.productfieldname.Dump() // LinqPad .Dump() shows value

passing linq anonymous result to iqueryable object for another query

I have two tables (tbPerson and tbDataLog) where I need to return Id from one table (tbPerson) after checking certain conditions on both. After this, this result should be passed to another query. My first query returns the Id (primary key of a table) successfully and I need to pass these ids to another query so that it return me data based upon these Id. I also has an IQueryable type base object to check certain conditions to fetch data.
IQueryable<tbPerson> dataset
and I cannot changes this from Iqueryable to other as it will break other part of the code)
My first linq statement:
public static IQueryable<LogResults> GetResultsForYes()
{
Databasename ents = new Databasename();
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new LogResults { _LogID = f.Id }).OrderBy(x => x._LogID);
return ids;
}
public class LogResults
{
public int _LogID { get; set; }
}
I access my result something like this where I can see in debugger all the Ids.
IQueryable<LogResults> log = GetResultsForYes();
Problem comes, when I tried to get records from tbPerson based upon these returned Id.
dataset=log.where(x=>x._LogID != 0);
I get this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable'. An explicit conversion exists(are you missing a cast)?
Any suggestions or some other good approach is welcome.
I love this thing about stackoverflow. when we write questions we force our brain to think more deeply and after 30 mins of posting this question, I solved it in a simple way. Sometimes we overcomplicated things!
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new { f.Id }).ToArray();
var allId = ids.Select(x => x.Id).ToArray();
dataset = dataset.Where(x => allId.Contains(x.Id));
#ankit_sharma : I have not tested yours but will give a try and come back to you. Thanks for giving time and effort.
IQueryable<tbPerson> dataset=log.where(x=>x._LogID != 0);
The result of log.where(x=>x._LogID != 0) is an IQueryable<LogResults>, and you are trying to assign this result to dataset of type IQueryable<tbPerson>, two diferent types.
EDIT:
I see you make a join to get the tbPerson ids, and then you do a second query to get the persons. You could get the persons in the first join.
I just modify your code:
IQueryable<tbPerson> persons = from person in ents.tbPerson
join g in ents.tbDataLog
on person.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select person;

Select top N elements and remember occurence's order

I have a requirement to select top N elements of related products from a big list of products.
So far, I have below code and it works perfectly.
class Product
{
public string Name;
public double Rating;
public List<Product> RelatedProducts;
public List<Product> GetTopRelatedProducts(int N)
{
var relatedSet = new HashSet<Product>();
var relatedListQueue = new Queue<List<Product>>();
if (RelatedProducts != null && RelatedProducts.Count > 0)
relatedListQueue.Enqueue(RelatedProducts);
while (relatedListQueue.Count > 0)
{
var relatedList = relatedListQueue.Dequeue();
foreach (var product in relatedList)
{
if (product != this && relatedSet.Add(product) && product.RelatedProducts != null && product.RelatedProducts.Count > 0)
relatedListQueue.Enqueue(product.RelatedProducts);
}
}
return relatedSet.OrderByDescending(x => x.Rating).Take(N).OrderBy(/*How to order by occurrence here? */).ToList();
}
}
Now, I want GetTopRelatedProducts method to remember the occurrence order of top N products. First added product to the HashSet will be at the begining of the returned List.
For example, if I have this scenario:
//...
relatedSet.Add(new Product(){Name="A", Rating=3});
relatedSet.Add(new Product(){Name="B", Rating=4});
relatedSet.Add(new Product(){Name="C", Rating=5});
//...
and if N = 2, the method should return : B,C instead of C,B because B was added first to the HashSet.
So I changed the return statement in the method to:
var relatedSetCopy = relatedSet.ToList();
return (from p in relatedSet.OrderByDescending(x => x.Rate).Take(N)
join c in relatedSetCopy on p.Name equals c.Name
let index = relatedSetCopy.IndexOf(c)
orderby index
select p).ToList();
Basically, I use LINQ Join to re-order the list in the same way it was before the ordering on Rating.
I want to do it this way because first added product has more similarity with selected product than others.
I have two questions here:
Is there a better way to re-order the returned list?
Is there a better design to handle relation between products? (I was thinking about implementing a tree structure. So object navigation and retrieval will be faster)
Is there a better way to re-order the returned list?
You can simply Intersect the relatedSet with the top N related reordered set because Intersect yields the items based on their order in the first sequence.
So instead of
return relatedSet.OrderByDescending(x => x.Rating).Take(N).ToList();
you would use
return relatedSet.Intersect(relatedSet.OrderByDescending(x => x.Rating).Take(N)).ToList();

Linq Query to check if the records are all the same

I am very much new to the Linq queries. I have the set of records in the csv which is like below
ProdID,Name,Color,Availability
P01,Product1,Red,Yes
P02,Product2,Blue,Yes
P03,Product1,Yellow,No
P01,Product1,Red,Yes
P04,Product1,Black,Yes
I need to check for the Names of the each product and if its is not the same in all the records then I need to send an error message.I know the below query is used to find the duplicates in the records but not sure how can I modify it check if it all has the same values.
ProductsList.GroupBy(p => p.Name).Where(p => p.Count() > 1).SelectMany(x => x);
var first = myObjects.First();
bool allSame = myObjects.All(x=>x.Name == first.Name);
Enumerable.All() will return true if the lambda returns true for all elements of the collection. In this case we're checking that every object's Name property is equal to the first (and thus that they're all equal to each other; the transitive property is great, innit?). You can one-line this by inlining myObjects.First() but this will slow performance as First() will execute once for each object in the collection. You can also theoretically Skip() the first element as we know it's equal to itself.
if I understand correctly you want to check if product exists in the list
using System.Linq;
private bool ItemExists(string nameOfProduct) {
return ProductsList.Any(p=> p.Name== nameOfProduct);
}
UPD after author comment:
To know all the records that are not having the same name as the first record:
var firstName = ProductsList[0].Name;
var differentNames = ProductsList.Where(p => p.Name != firstName);
Another option (just to have all other names): ProductsList.Select(p => p.Name).Where(n => n != firstName).Distinct()
Old version
So, if there are at least two different names then you should return an error?
LINQ way: return ProductsList.Select(p => p.Name).Distinct().Count() <= 1
More optimizied way:
if (ProductsList.Count == 0)
return true;
var name = ProductsList[0].Name;
for (var i = 1; i < ProductsList.Count; i++)
{
if (ProductsList[i].Name != name)
return false;
}
return true;

Condition in Linq Expression

I have a LINQ expression that joins two tables. I want to conditionally check another boolean value: (notice text between ********* below)
bool status = testfunc();
var List =
from t in Houses
join k in Tbl_HouseOwner on t.HouseCode equals k.HouseCode
where k.ReqCode== t.ReqCode
*********** if (status) { where k.Name.Contains(Name) } **********
select new
{
...
name = k.Name,
...
};
You can use status to mask the condition, like this:
where k.ReqCode == t.ReqCode && (!status || k.Name.Contains(Name))
If the status is false, the OR || will succeed immediately, and the AND && will be true (assuming that we've got to evaluating the OR ||, the left-hand side of the AND && must have been true). If the status is true, on the other hand, the k.Name.Contains(Name) would need to be evaluated in order to finish evaluating the condition.
An alternative option to dasblinkenlight's answer (which should work fine) is to build up the query programmatically. In this case you're effectively changing the right hand side of the join, so you could use:
IQueryable<Owner> owners = Tbl_HouseOwner;
if (status)
{
owners = owners.Where(k => k.Name.Contains(Name));
}
Then:
var query = from t in Houses
join k in owners on t.HouseCode equals k.HouseCode
where k.ReqCode == t.ReqCode
select new { ... };
Which approach is the most suitable depends on your scenario. If you want to add a variety of different query filters, building it up programmatically can be cleaner - and make the final SQL easier to understand for any given query. For a one-off, dasblinkenlight's approach is simpler.
Also note that in LINQ to Objects at least, it would be more efficient to join on both columns:
var query = from t in Houses
join k in owners
on new { t.HouseCode, t.ReqCode } equals new { k.HouseCode, k.ReqCode }
select new { ... };
In any flavour of LINQ which translates to SQL, I'd expect this to be optimized by the database or query translation anyway though.
I do it this way:
IQueryable<X> r = from x in Xs
where (x.Status == "Active")
select x;
if(withFlagA) {
r = r.Where(o => o.FlagA == true);
}
To fit this to your example, firstly you could do this:
IQueryable<YourOwnerType> filteredOwners = Tbl_HouseOwner;
if( status ) {
filteredOwners = filteredOwners.Where( o => o.Name.Contains(Name) );
}
Then substitute Tbl_HouseOwner with filteredOwners.
var List =
from t in Houses
join k in filteredOwners on t.HouseCode equals k.HouseCode
where k.ReqCode== t.ReqCode
//Nothing here
select new
{
...
name = k.Name,
...
};
Now, you may know this, but the point here is that the initial .Where does not 'reach out' to the database. Your query doesn't get executed either until you start enumerating it (e.g. foreach) or call a method like ToList(), First(), FirstOrDefault(). This means you can call .Wheres after your selects if you prefer and the final query will still be efficient.

Categories