Linq getting a list from another list - c#

I have two collections: one is Items And Another is ActiveItems
The only intersection between these two collection is Name
I want a list with Linq from Items where the Items names are in the ActiveItems with that name
I wrote this code is there a better idea:
Items.Where(i => ActiveItems.Count(v=> v.Name==i.Name) > 0)

I would probably create a set of the names from ActiveItems and then use that:
var activeNames = new HashSet<string>(activeItems.Select(x => x.Name));
var itemsWithActiveNames = items.Where(x => activeNames.Contains(x.Name))
.ToList();
Another option is to use a join, e.g. with a query expression:
var query = from activeItem in activeItems
join item in items on activeItem.Name equals item.Name
select item;
Note that this will give duplicate item values if there are multiple ActiveItem values with the same name. Another alternative join, which doesn't have this problem but is a bit clumsier:
var query = from item in items
join activeItem in activeItems
on item.Name equals activeItem.Name
into g
where g.Any()
select item;
Note that all of these will avoid the O(N * M) check for names - they'll all use hash tables behind the scenes, to give an O(N + M) complexity.

Items.where(i => ActiveItems.Any(a => i.Name == a.Name))

var results = from i1 in collection1.Items
join i2 in collection2.ActiveItems on i1.Name equals i2.Name
select i2.Name;

Using a join:
from item in Items
join active in ActiveItems on item.Name equals active.Name
select item

Related

Fields not visible after groupby?

What I have now:
var batch_pymnts2 = (from a in ctx.WarehouseStatementBatchPayments
join b in ctx.WarehouseStatementBatches on a.WarehouseStatementBatchID equals b.ID
join c in ctx.WarehousePaymentInvoices on a.ID equals c.WarehouseStatementBatchPaymentID
where b.ID == batchID
select new
{
PaymentId = a.ID,
PaymentNet = a.Net,
PaymentType = a.Type
})
.GroupBy(d => d.PaymentId).Where(x => x.Count() == 1);
I need to query these results like so:
var test = (from a in batch_pymnts2 where a.PaymentNet > 100 select a).ToList();
However, I cant see the fields of the (anonymous) type that the first statement uses to project the results into.
Will I need to use a defined type in the query for the projection? Is there a way to do it with anonymous types?
[update]
I managed to change the source query a bit, moving the group by inside and before the group by. This lets the fields of the anonymous type being projected, be "exposed" in further statements.
var count2 = (from a in WarehouseStatementBatchPayments
join b in WarehouseStatementBatches on a.WarehouseStatementBatchID equals b.ID
join c in WarehousePaymentInvoices on a.ID equals c.WarehouseStatementBatchPaymentID
group a by a.ID into grp
from d in grp
where d.WarehouseStatementBatchID == batchID && grp.Count() == 1
select new { PaymentId = d.ID, PaymentNet = d.Net, PaymentType = d.Type }).ToList();
batch_pymnts2 is a sequence of group objects. In effect, it is a collection of collections of your anonymous type. Each item in batch_pymnts2 has this:
group.Key; /* a PaymentId value */
((IEnumerable)group); /* the anon type items grouped together in this group */
Those group objects implement the IGrouping interface. Their Key property is the PaymentId values that define the groups. If you enumerate the groups (they implement IEnumerable<T>), you'll get the anonymous objects that you grouped by PaymentId:
var test = batch_pymnts2.SelectMany(g => g.Where(anon => anon.PaymentNet > 100));
test is now an enumeration of your anonymous type, because we have now enumerated a subset of the anon items from each of the groups, and (in effect) unioned all those little enumerations of anon back into one big one.
If you want to select groups which have at least one anonymous thingy with PaymentNet > 100, try this:
// Groups which have at least one PaymentNet > 100
var t2 = batch_pymnts2.Where(g => g.Any(anon => anon.PaymentNet > 100));
// PaymentIds of the groups which have at least one PaymentNet > 100
var ids = t2.Select(g => g.Key);
// PaymentIds that appear only once
var singles = t2.Where(g => g.Count == 1).Select(g => g.Key);
I don't know why you're grouping them, or what your PaymentNet > 100 query is meant to accomplish, so I'm not sure exactly how to write the query you want. But your starting point is that you're querying a sequence of group objects which contain enumerations of your anonymous type -- not a sequence of that type itself.

Group by with linq to update collection

I have a IEnumerable collection called allEventsDetails and collection properties
are Digit, Name and status.
I need to update the same collection if Two or more Digits values are equal.
var categories = from p in allEventsDetails
group p by p.Digit into g
where g.Count() > 1
select g.Select(s => s.status = "true");
How can I use linq to do this?
you could do something like this:
(from x in allEventsDetails
group x by x.Digit into y
where y.Count()>1
select y).SelectMany(z=>z).ToList().ForEach(x=>x.Status="true");
What you could also do is:
var temp= (from x in allEventsDetails
group x by x.Digit into y
where y.Count()>1
select y).SelectMany(z=>z);
foreach (var x in temp)
{
x.Status= "test";
}
This way you don't have to create a "temporary" list.

How to join a list of lists in a unique list using Linq

I have an List of objects that have a List of long. I need to get all long of all objects and join it to a unique list.
How to do this using System.Linq?
This is my code actually (with this code I get a List of List of long)
var result = LIST1.Select(x => x.LIST2.Select(y => y.Id).ToList()).Tolist();
This flattens your list and then does a distinct on it
LIST1.SelectMany(a => a.LIST2.Select(b => b.Id)).Distinct();
Use selectMany instead of Select
var result = LIST1.SelectMany(x => x.LIST2.Select(y => y.Id)).Tolist();
Just dont get it if you need to join the lists of objects or you need to union all, but for join you could use:
var result = (from Item1 in LIST1
join Item2 in LIST2
on Item1.Id equals Item2.Id
select new { Item1, Item2 }).ToList();

Group a collection and take first 5 from each group

I have a collection. I need to group the collection by "A" property. And I have to sort each group by"B" property. Then select first 5 from each group.
Can anyone suggest a LINQ query for this?
The way I tried is not working.
(from item in Recipes
orderby item.Rating descending
group item by item.MainCategory).Take(5)
The query should return IEnumerable<IGrouping<string, myrecipetype>>
You are taking first five groups. Instead you need to select first five items from each group:
from item in Recipes
orderby item.Rating descending
group item by item.MainCategory into g
select g.Take(5)
UPDATE:
from item in Recipes
orderby item.Rating descending
group item by item.MainCategory into g
select g.Take(5).GroupBy(item => item.MainCategory).First()
Edit: In your case, with sorting added (after the OP was updated):
Recipes.GroupBy(recipy => recipy.MainCategory)
.Select(recipyGroup => recipyGroup.OrderBy(recipy => recipy.Rating)
.Take(5))

Eliminate loop and use Linq instead

Say I have a List as below:
List<R> lstR = GetR();
Now I want a Linq statement to get menus assigned to R, I achieved this by using a loop and then using Linq to get the menus as below:
List<int> ids = new List<int>();
foreach (R r in lstR)
{
ids.Add(r.Id);
}
menu = (from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText).Distinct();
Now as far as I know the above is two loop(Linq is using internal loop). would I be able to combine these two statements i.e. not do the first loop to get the ids?
In both lstR and db.Menu are either in-memory data sets (Linq-to-Objects) or IQueryable collections from your database, you can do this:
menu =
(from s in db.Menu
where lstR.Select(r => r.Id)
.Contains(s.R.Id)
select s.MenuText)
.Distinct();
Or this:
menu =
(from s in db.Menu
join r in lstR on s.R.Id equals r.Id
select s.MenuText)
.Distinct();
However, since List<R> exists in memory and db.Menu is an IQueryable, you're options are limited. You could materialize db.Menu into an IEnumerable, so you can process it in memory:
List<R> lstR = GetR();
menu =
(from s in db.Menu.AsEnumerable()
join r in lstR on s.R.Id equals r.Id
select s.MenuText)
.Distinct();
But, this can be costly if there are a lot of records. It's better to do something like this, which admittedly doesn't look much different from what you already have:
List<R> lstR = GetR();
var ids = lstR.Select(r => r.Id).ToList(); // or .ToArray();
menu =
(from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText)
.Distinct();
But in truth, the best option is to see if you can refactor GetR so that it returns an IQueryable<R> from your database. That way you can use both of the first two options without needing to materialize any sets into memory first. And by the way, once you've done that and set up navigation properties, you can probably do something like this:
IQueryable<R> lstR = GetR();
menu =
(from r in lstR
from s in r.Menus
select s.MenuText)
.Distinct();
It can be done like.
menu = (from s in db.Menu
where lstR.Select(item => item.Id).Contains(s.R.Id)
select s.MenuText).Distinct();
But i wouldnt combine those two statements, because if you use a HashSet it will speed up:
var ids = new HashSet<int>(lstR);
menu = (from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText).Distinct();
This will be faster i guess. The problem with the first one is, every s in db.Menu The list is iterated for creating a list of id's select().
You coud use the linq projection method Select():
ids = lstR.Select(p => p.Id);
menu = db.Menu.Where(s => GetR().Select(r => r.Id).Contains(s.R.Id))
.Select(s => s.MenuText)
.Distinct();
but it will be to complex. It will be better if you'l write like this
var ids = GetR().Select(r => r.Id);
menu = db.Menu.Where(s => ids.Contains(s.R.Id))
.Select(s => s.MenuText)
.Distinct();
Use Join
var result = (from s in db.Menu
join r in lstR on s.Id equals r.ID
select s.MenuText).Distinct();

Categories