Multiple randomly selection Linq - c#

I have Questions table and List of CategoryIds. I have to get one question from each CategoryId randomly. Right now I'm doing like this:
var randomQuestions = new List<Question>();
foreach(int id in categoryIds){
randomQuestions.add(questions.Where(o => o.CategoryId== id).OrderBy(o => Guid.NewGuid()).FirstOrDefault());
}
But is there way to do it only with Linq? Thanks

This should do what you want in one query
var randomQuestions = questions
.Where(q => categoryIds.Contains(q.CategoryId))
.GroupBy(q = > q.CategoryId)
.Select(grp => grp.OrderBy(_ => Guid.NewGuid()).First())
.ToList();
That will first filter only questions in the categories you care about, then it groups on the category id and for each group it randomly picks one.

Related

Selecting parents of children who meet certain criteria

So, I have retrieve a bunch of Countries from my database:
var countries = dbContext.Countries.toList();
Then, depending on program flow, I filter further on these Contries:
var asianContries = result.where(c=>c.continent == "asia").toList();
My Countries table is a "parent" table to a Cities table, with each city relating to a country. The cities table contains population info, which I further want to filter on.
I want, from the already filtered "asianCountries" list object, a list of countries in which there are cities with more than 500,000 people. I just went stone-cold in trying to figure out how to do it. Also, I'm new to this stuff.
Why this multistep-filtering instead of selecting on all criteria in one go? Complexity of program flow. Long story. :-)
If i understood correctly you have filtered to Asian countries already now you want to filter those results further.
I would do it one of two ways either if you have the population as int
var cities = asianCountries.Select(x => x.cities.Where( y => y.population > 500000)).ToList();
if it is a string then
var cities = asianCountries.Select(x => x.cities.Where(y => Convert.ToInt32(y.population) > 500000)).ToList();
This should work i think.
Example for joining multiple tables ,
from ct in dbContext.Countries
join ci in dbContext.Cities on ct.CityID equals ci.ID
where (ct.continent == "asia") && (ci.Population == // yourCondition)
select new { country = ct.Name, city = ci.Name , // other fields you want to select
};
You could take a reference how to join multiple tables here
If an object implements IQueryable<T> the query is only executed when the object is enumerated. This means that you can chain queries together and execution will be deferred until you call, for example ToList().
In your example you could do something like:
// to select the cities
var largeCities = dbContext.Countries
.Include(t => t.Cities)
.Where(c=> c.continent == "asia"
&& c.Cities.Population > 500000)
.Select(c => c.Cities).ToList();
// EDIT
// to select the countries that have these cities
var countries = dbContext.Countries
.Include(t => t.Cities)
.Where(c=> c.continent == "asia"
&& c.Cities.Population > 500000)
.ToList(); // remove .Select(c => C.Cities) if you want the countries
Or
var largeCities = asianCountries
.Where(c => c.Cities.Population > 500000)
.Select(c => c.Cities)
.ToList();

c# linq join with projection

So in my shop app i'm allowing users to favorite items.
what i wish to do is to show the list of favorite items in the user profile page where the list is sorted on the like date in a descending order.
We have 2 tables that we need to join, one is items and the other one is favorites.
how would one join this two tables, so the result will answer this criteria:
The result will be a list of items that was favorite by this particular user.
The results will come with the list of comments for each item (each item have a list of comments).
The results will be sorted correctly.
So far i came up with this:
Items =
await _context.Favorites
.Join(
_context.Items,
f => f.ItemId,
i => i.Id,
(f, i) => new { f, i })
.Distinct()
.OrderByDescending(x => x.f.FavDate)
.Select(x => x.i)
.Skip(skip).Take(take)
.Include(c => c.ListOfComments)
.ToListAsync();
This works but does not answer the first criteria, which is that only items favorite by particular user will be returned, this returns list of items favorite by the users and not by a particular user.
I tried to add a where clause before the join (_context.Favorites.Where(f.UserVoterId.equals(profileId)) but it throws an exception.
One way to approach this is to:
include the user id as the join key and
load the data in separate steps
To select the favorite items of a specific user (profileId) you need this query:
var favorites = _context.Favorites.OrderByDescending(f => f.FavDate)
.Join(_context.Items,
fav => new { fav.ItemId, UserId = fav.UserVoterId },
item => new { ItemId = item.Id, UserId = profileId },
(fav, item) => item)
.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToList();
And to load the comments just try one of the following (whichever works):
var itemIds = favorites.Select(f => f.Id);
var comments = _context.Comments.Where(c => itemIds.Contains(c.ItemId))
.GroupBy(c => c.ItemId)
.ToDictionary(g => g.Key, g => g.ToArray());
Or
var items = _context.Comments
.GroupJoin(favorites,
comment => comment.ItemId,
favorite => favorite.Id,
(fav, comments) => new
{
Item = fav,
Comments = comment.ToArray()
});
In the first case, the comments are added to a Dictionary<TItemId, Comment> where TItemId is the type of item.Id and to get the comments for an item you'd use
var itemComments = comments[item.Id];
which is a O(1) operation.
In the second case the items collection will have all the data you need so you'll have to project it into the structure that suits your needs.
NB I mentioned earlier whichever works because I'm not entirely sure that GroupJoin is properly translated to SQL and I'm not sure if I missed some requirements for the GroupJoin method.

Select from a table with Entity Framework

I have 3 tables:
Person
Groups
PersonsGroups
I need to select all persons when they are in the group from a list
(list from few groups)
I tried to select like this:
var tdd = GS.PersonsGroups.Include("Person")
.Where(r => s.Contains(r.GroupID.Value))
.Select(c => c.Person);
But it is not Person is it PersonGroup and I don't have all Person prop
What is the right way to get it?
Thank you very much in advance
If I understand correct, you are seeking for something like this
List<int> groupIds = ...;
var query = db.Persons
.Where(p => p.PersonGroups.Any(pg => groupIds.Contains(pg.GroupID.Value));

Join an array of string with the result of an existing linq statement

As a follow up to my last question here:
Filtering a list of HtmlElements based on a list of partial ids
I need to take this statement:
doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
and join it with an array of strings called fields. Assuming the array and list will have the same amount of elements each and line up correctly. I tried using Zip() but thought I might need to use an additional linq statement to make it work.
Assuming that fieldList[0] and IdList[0] corresponding to each other, you can do the following:
var IdList = doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
var resultList = fieldList
.Select( (item, index) => new { Field = item, Id = IdList[index] })
.ToDictionary(x => x.Id, x => x.Field);
You have mentioned it already, you can use Enumerable.Join:
var joined = from id in fields
join ele in elements on id equals ele.Id
select new { Element = ele, ID = id };
var dict = joined.ToDictionary(x => x.ID, x => x.Element);
I've presumed that you want to join them via ID. I've also presumed that the string[] contains only unique ID's. Otherwise you need to use Distinct.

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories