LINQ - Where a list contains an element of another list - linq

I have two SQL tables: Movies and Tags, while Movies contains a List<Tag>
Now I want to find all movies that have at least one of the tags of a List<Tag> argument. How can I do that in LINQ?
public IEnumerable<Group<Genre, Movie>> GetMoviesGrouped(string search, List<Tag> tags)
{
var movies = from movie in Movies
where ( movie.Name.Contains(search)) && movie.Tags.???contains any element of tags???
group movie by movie.genre into g
select new Group<Genre, Movie> { Key = g.Key, Values = g };
....
}

Movies where any of the tags is contains in tags:
var movies = from movie in Movies
where ( movie.Name.Contains(search))
&& movie.Tags.Any(t => tags.Contains(t))
group movie by movie.genre into g
select new Group<Genre, Movie> { Key = g.Key, Values = g };
However since this is comparing Tag instances and not strings:
It probably won't get translated to SQL (which means you will have to hydrate the query and do the extra filtering in Linq-to-Objects), and
You may want to compare the data of the tags rather than instances (unless you have overridden Equals on Tag
something like:
where movie.Tags.Any(mt => tags.Any(t => t.ID == mt.ID)) // or whatever property(ies) of `Tag` defines equality

You can intersect both list and see if there is any element like:
&& movie.Tags.Intersect(tags).Any()
Since Tag is an object, It will compare the references. Instead you can select unique identifier from Tag and then intersect that like:
&& movie.Tags.Select(r=> r.ID).Intersect(tags.Select(t=> t.ID)).Any()
Where ID is the primary key of Tag table.

Related

Fetch data using LINQ query with left join and AsExpandable()

I have following two tables: Images and Articles
Both the tables are linked by the ImageId column.
An image can be associated with multiple articles.
For example: Images table has 100 rows and Articles table has 200 rows.
Out of these 100 images assume that only 90 are used across the Articles. In this case some of the images are repeated across many articles.
Here I want to fetch the unused 10 images (from Images table) and also want to include the ones that are associated with articles not more than 2 times. I want to ignore those images which are associated with the articles more than 2 times.
I tried the below linq query but it is not working for me.
var predicate = PredicateBuilder.True<Image>();
if (type != null && type != 0)
{
predicate = predicate.And(c => c.ImageType == type);
}
if (!string.IsNullOrWhiteSpace(keyword))
{
predicate = predicate.And(c => c.Name.Contains(keyword) || c.Keyword.Contains(keyword));
}
int skip = numberofImages * (page - 1);
var images = (from imgs in context.Images
join art in context.Articles on imgs.ImageId equals art.ImageId into unusedImages
from img in unusedImages.DefaultIfEmpty()
group img by imgs.ImageId into grouped
.AsExpandable()
.Where(predicate)
orderby Guid.NewGuid(), imgs.ImageId descending
select imgs)
.Skip(skip).Take(numberofImages).ToList();
Can any one help me to fix this issue?
split your query something like this,i wont complicate what is preferably simple
var usedArticles = (from item in context.Articles
group item by imageid into newList
select new
{
imageid = newList.key,
count =newlist.count
}).ToList();
var unwaantedImageIds = usedArticles.where(x=>x.count>2).Select(y=>y.imageId).ToArray();
var unwantedImages =context.image.where(x=>unwaantedImageIds.contains(x.imageId));
var result = context.images.Except(unwaantedImageIds).ToList();

LINQ Left Outer Join with Greater Than and Less Than Date Conditions

I've been struggling with this for a while and can't find the syntax for a LINQ outer join that has multiple conditions based on date. I've been looking into the GroupJoin syntax, but that only let's you compare one field value (normally IDs).
I would like to test if the parent table has a date (e.g. "UpdateDate") that falls within multiple values defined in the child table (e.g. "StartDate" and "EndDate"). If the parent date fits the condition, pull a column or two from the child table. If not, those columns from the child table should be null (classic left join stuff).
I don't think query syntax will work because it only recognizes equijoins.
Is there a way to do this in LINQ using Lambda syntax? I've been trying to use some combination of "SelectMany" and "DefaultIfEmpty" but keep getting stuck trying to define the join.
The way to do this in linq:
var q = from a in TableA
from b in TableB.where(x => a.Date > x.StartDate && a.Date < x.EndDate).DefaultIfEmpty()
select {...}
Use parameter ResultSelector of Queryable.GroupJoin to select what you want:
var result = dbContext.Parents.GroupJoin(dbContext.Children,
// outer and inner key Selectors:
parent => parent.Id, // from every parent take the primary key
child => child.ParentId, // from every child take the foreign key to parent
// ResultSelector: take the parent and all his children to make one new object
(parent, children) => new
{
// Select only the Parent properties you actually plan to use:
Id = parent.Id,
Name = parent.Name,
...
Children = children.Select(child => new
{
// select only Child properties you plan to use:
Id = child.Id,
// No need: you know the value: ParentId = child.ParentId,
...
"If the parent date fits the condition, pull a column or two from the child table, otherwise those columns from the child table should be null "
SpecialColumnA = (parent.BirthDay.Year < 2000) ?? child.BirthDay : null,
SpecialColumnB = (parent.Name == "Kennedy" ?? child.Name : null,
});
If the conditions are the same for a lot of columns, consider to check this only once:
SpecialColumns = (parent.Birthday.Year >= 2000) ? null :
// else fill the special columns:
new
{
Name = child.Name,
SomeWeirdProperty = parent.Id + child.Id,
...
},
});

Dynamic Linq Library can’t handling duplicate alias column names

I am trying to collect the data from database by using Dynamic Linq Library NeGet. When I loop through it showing this error ‘The item with identity 'FirstName' already exists in the metadata collection’
Seems like Dynamic Linq Library NuGet is can’t handle duplicate alias column names.
I have two tables and it has One-On-Many relationship as follow, containing the same Columns names in Lead table and CoBorrower table.
Table1: Lead
Columns: LeadID, FirstName,DateCreated
Table2: CoBorrower
Columns: LeadID, CoBorrowerID,FirstName,Tax,DateCreated
Here is my code snippet
var res = (from l in db.Leads
join db.CoBorrower.GetAll()
on l.LeadID equals cb.LeadID
select new { l, cb }).AsQueryable();
string myCustomCondition="FistName=myname";
IQueryable iq = res.Where(myCustomCondition)
.OrderBy(reportBulder.Group1)
.Select("new(l.LeadID,l.FirstName,cb.FistName)")
.GroupBy("LeadID", "it")
.Select("new (it.Key as Key, it as Value)");
foreach (dynamic group in iq)
{
string Key = group.Key.ToString();
List<dynamic> items = new List<dynamic>();
foreach (dynamic album in group.Value)
{
items.Add(album);
}
dataList.Add(Key, items);
}
I will appreciate your help in advance.
This is logical. The .Select("new(l.LeadID,l.FirstName,cb.FistName)") will create an anonymous object like:
new
{
LeadID = ....,
FirstName = ....,
FirstName = ....,
}
that is illegal (two properties with the same name)
Use the as
.Select("new(l.LeadID,l.FirstName,cb.FistName as FirstName2)")
so that the anonymous object created is
new
{
LeadID = ....,
FirstName = ....,
FirstName2 = ....,
}
as you do in the second .Select.

LINQ - list group by

I am having trouble with a class that has a list of objects - basically it is a shopping cart where you can add items. I wish for the items to be grouped by the item id, so they would not appear more times in the cart.
So if you add an item, and then you add it again, it should appear as 1 row, quantity =2, instead of 2 rows quantity 1.
First of all, is there a way of automatically organizing a list this way?
If not, then I need another var:
var l = from i in list.cartItems
group i by i.id into g
select g.ToList();
list.cartItems = l;
list is the object, cartItems is the list, l would be the new list to "refresh"
However, this causes me an error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable>' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
Any ideas?
Thanks
Edit:
I edited the error
Cartitems is just:
public List cartItems;
inside a CartItemList class
You should specify the grouped element you want, like this:
list.cartItems = l.First();
Your cartItems is a 'ListOfItems' type, and your var l is a new grouped type, which is 'int,ListOfItems' type. So to cast your 'int,ListOfItems' to cartItems which is 'ListOfItems', you should choose the element some element from your result collection.
for solve you need change select clause in your query - in select you can apply aggregate function like this:
var l = (from i in list.cartItems
group i by i.id into g
select new CartItem(){Id=g.Key, quantity=g.Count()}).ToList();
list.cartItems = l;
try sample that work
var t = Enumerable.Range(1, 10).Select(i => new { Id = i % 3, Q = 1 }).ToList();
var b = from i in t
group i by i.Id into g
select new { Id = g.Key, Q = g.Count() };
t = b.ToList();
and check your inputs

Any way to add a property using linq?

So I have this list, it returns an ID and a thumbnail. ex. List<PersonPicture>
and I have this list, List<Person> which has a property named "picture" in it.
Is there anyway that I can merge this two properties and add the List<PersonPicture> to the property named "picture" in it and base this via the ID since they have the same?
Any help would be appreciated.
You can use an anonymous object for this, below an example:
List<PersonPicture> pictures = LoadPictures();
List<Person> persons = LoadPersons();
var result = persons.Select(pers => new
{
Id = pers.Id,
Name = pers.Name,
Picture = pictures.Where(pic => pic.PersId == pers.Id)
.FirstOrDefault()
.Thumbnail
};
Another solution is to use a Join:
var result = persons.Join(pictures,
pers => pers.Id,
pic => pic.PersId,
(pers, pic) => {
return new
{
Id = pers.Id,
Name = pers.Name,
Picture = pic.Thumbnail
};
});
LINQ isn't quite designed for modifying existing collections like this, but you can do it:
foreach (tup in people
.Join(
picture,
person => person.ID,
picture => picture.ID,
Tuple.Create
))
{
tup.Item1.Picture = tup.Item2;
}
EDIT: Note that this will produce unpredictable results if a person has more than one picture. Is this a possibility, and how should it be dealt with?
You could either use a Join or the Zip operator in linq. These links will take you to questions about the syntax of using both of them. Basically the Join just adds the two lists together based on a key just like in SQL and the Zip merges the two lists by matching the position of each element in each list..
You want to join the two lists based on a shared key -- the ID.
Basically, you want to use the Join operator in LINQ to find pairs of Person and PersonPicture that match the same ID:
persons.Join(pictures, // join these two lists
person => person.Id, // extract key from person
personPicture => personPicture.PersonId, // extract key from picture
(person, personPicture) => ??? // do something with each matching pair
The question you now face is what to do with each matching pair; Join lets you supply a delegate that takes a matching pair and returns something else, and the result of the Join operation will be a list of those 'something else's produced from each of the matching pairs.
Your problem is that you want to take each pair and do something with it -- specifically, you want to copy the picture from the PersonPicture object to the Person object. Since LINQ is all about finding data but not modifying it, this is not trivial.
You can do this in two ways. One is to create a temporary object from each pair, and then iterate over that and do your thing:
var pairs = persons.Join(pictures,
person => person.Id,
personPicture => personPicture.PersonId,
(person, personPicture) => new { person, personPicture };
foreach (var pair in pairs)
pair.person.Picture = pair.personPicture.Thumbnail;
(You can use a Tuple instead of a temporary object, as was suggested in another answer).
This works, but seems clumsy because of the temporary object (be it an anonymous object or a tuple).
Alternatively, you can do the assignment right inside the delegate, and return the Person object itself, since you're done with the PersonPicture object:
var personsWithPicturesPopulated = persons.Join(pictures,
person => person.Id,
personPicture => personPicture.PersonId,
(person, personPicture) => {
person.Picture = personPicture.Thumbnail;
return person;
});
This has the added bonus of giving you the list of persons for which you found a match in the personPictures list, omitting the ones without a match; this is sometimes exactly what you need (and other times it isn't, in which case you can discard the result of the join).

Resources