Using LINQ to combine similar rows of grouped data - linq

I've got some data in a table that looks like so:
Recipe | Category | Email
What I'd like to do is pull this data back from the source and put it into something that looks like so:
public class RecipeItem
{
public long Recipe { get; set; }
public long Category { get; set; }
public List<string> Names {get; set; }
}
Grouping by the Recipe and Category ids and putting all the emails that into the list.
So, what I've tried is to do something like this:
var recipeItems =
from entry in list
group entry by new { entry.Recipe, entry.Category}
into aRecipe
select new RecipeItem()
{
Recipe = aRecipe.Key.Recipe,
Category = aRecipe.Key.Category,
// ? Not sure how to stick the list of names in here
};
list is the data pulled back via entity framework.
But this isn't quite right - I think I'm close here (maybe). What am I missing here on this?
Follow-up:
Thanks to Aducci for clearing this up. The answer is that you can do this:
Names = aRecipe.Select(x => x.Name)
and this will add all those Names which are in each group into the Names collection for that group. Pretty nifty.

I would modify your class to look like this
public class RecipeItem
{
public long Recipe { get; set; }
public long Category { get; set; }
public IEnumerable<string> Names {get; set; }
}
And your link to entities query to:
var recipeItems =
from entry in list
group entry by new { entry.Recipe, entry.Category}
into aRecipe
select new RecipeItem()
{
Recipe = aRecipe.Key.Recipe,
Category = aRecipe.Key.Category,
Names = aRecipe.Select(x => x.Name)
};

Related

Linq Group By - Unable to group on custom classes?

I have noticed that when I do a LINQ - group by, it only seems to be working if I dont group on any of my custom classes.
I have a Product class (shown below) and I would like to group on Product.Id ,Product.Variant (object) and on Product.Options (ICollection) (because my source list contains multiple times the same Product but with different Variants and / or Options)
Product:
public class Product
{
public int Id { get; set; }
public Variant Variant { get; set; }
public ICollection<Option> Options{ get; set; }
public string Name { get; set; }
public int Amount { get; set; }
}
The code below will do a grouping, but only on my Product.ID, when I try to also group on Variant / Options, I get no grouping (well, not the grouping I am intending to make) as it will return just as much items as my source list.
IEnumerable<productAndSum> productsAndSums = unmappedProducts
.GroupBy(prod => new { Id = prod.Id})
.Select(group => new productAndSum()
{
Key = group.Key,
Sum = group.Sum(x => x.Amount)
});
If I am on the right track and the issue is related to the Objects, then it might be usefull to add that also Option and Variant have multiple objects and collections themselves, or is this too deep?
Extra information: I first started to just group by my Product class (not Id, Variant & Options separately), but this was unsuccessful. so I started eliminating properties and this is how I found out this issue. I think that solving this issue will result in killing two birds with one stone.
Warm regards

LINQ search Product Index

I have a List<Product> contains properties of Bikes (Name, ProductName, Color, List Price). I'm struggling to figure out how to write a search function using LINQ. I'd like to find a name of Bike. Any suggest will be help me some ways.
Imagine that your name is taken from a variable called nameToSearch.
This is if you want to get the Product.
string nameToSearch = "BikeName";
List<Product> list = bikes.Where(x => x.Name == nameToSearch).ToList();
I assume you have the following Product class:
public class Product
{
public String Name { get; set; }
public String ProductName { get; set; }
public String Color { get; set; }
public String List { get; set; }
public String Price { get; set; }
}
You also mentioned you have your data in a List<Product>. I will give a demo name for it:
List<Product> myProductList = GetProductList();
// Where GetProductList() will create a new List<Product> and populate it.
String bikeNameFilter = GetNameFilter();
// You can chnage this by the string you want for filtering.
You can use the following to get your data:
List<Product> myFilteredProductList = (from p in myProductList
where p.Name = bikeNameFilter
select p;
).ToList()
Obviously you can change the filter you want to use to another property of your product. Finally to get the actual name, you can loop through the list you just got:
foreach (var p in myFilteredProductList)
{
Console.WriteLine(p.ProductName);
// Use this value wherever you want.
}
Take a look at a nuget package I have created
http://www.nuget.org/packages/NinjaNye.SearchExtensions
This will enable the following (and more) which will return results where the search term appears in any of the properties specified
var result = products.Search("searchTerm", p => p.Name, p => p.ProductName);
Performing a search against all string properties can be done as follows:
var result = products.Search("searchTerm");
Alternatively, you can perform an AND search where the search term exists in a set of properties as follows:
string searchTerm = "searchTerm";
var result = products.Search(searchTerm, p => p.Name)
.Search(searchTerm, p => p.ProductName);
For more information take a look at the projects GitHub page or my blog posts
UPDATE: don't forget the using directive...
using NinjaNye.SearchExtensions

Include() only specific property

Here I am retrieving items and including the creator of the item. The goal is to include only the first and last name from the creator, not the entire user model.
var items = _db.Items.Include("Creator")
The item model has Creator as a navigation property like this:
public User Creator { get; set; }
It works fine, but it loads the entire user model, when really I just want the first name and last name.
How do I specify I only want specific property returned from the user model?
You cannot do that using Include. You can use Select instead:
var items = _db.Items.Select(i => new { Item = i, Creator = new { i.Creator.FirstName, i.Creator.LastName } });
Update
If you need to return that query as method result you have to create a class which could hold the results:
public class ItemWithCreatorNames
{
public Item Item { get; set; }
public string CreatorFirstName { get; set; }
public string CreatorLastName { get; set; }
}
var items = _db.Items.Select(i => new ItemWithCreatorNames { Item = i, CreatorFirstName = i.Creator.FirstName, CreatorLastName = i.Creator.LastName });

Include nested entities using LINQ

I'm messing around with LINQ for the first time, and I'm using EF 4.1 code first.
I have entities containing nested Lists of other entities, for example:
class Release
{
int ReleaseID { get; set; }
string Title { get; set; }
ICollection<OriginalTrack> OriginalTracks { get; set; }
}
class OriginalTrack
{
int OriginalTrackID { get; set; }
string Title { get; set; }
ICollection<Release> Releases { get; set; }
ICollection<OriginalArtist> OriginalArtists { get; set; }
}
class OriginalArtist
{
int OriginalArtistID { get; set; }
string Name { get; set; }
ICollection<OriginalTrack> OriginalTracks { get; set; }
}
I'm wondering what is the quickest way, in one LINQ query, to obtain all the information for where ReleaseID == some value.
I've done my homework, but have found solutions that require implicit rebuilding of an object (usually anonymous) with the required data. I want the data out of the database in the exact format that it is held within the database, i.e. pulling a Release object with relevant ReleaseID pulls and populates all the OriginalTrack and OriginalArtist data in the Lists.
I know about Include(), but am not sure how to apply it for multiple entities.
All help greatly appreciated.
Use Include. This is the purpose of Include, and there's no reason to write a bunch of nested select statements.
context.Releases.Include("OriginalTracks.OriginalArtist")
.Where(release => release.ReleaseID == id);
This is simpler to write, simpler to read, and preserves your existing data structure.
To use Include you need to specify the name of the property you want to return - this means the name as it exists in your code, not in the database. For example:
.Include("OriginalTracks") will include the OriginalTracks property on each Release
.Include("OriginalTracks.OriginalArtist") will include OriginalTracks property on each Release, and the OriginalArtist on each Track (note that it's not possible - syntactically or logically - to include an OriginalArtist within including the OriginalTrack)
.Include("OriginalTracks").Include("OtherProperty") will include the OriginalTracks and OtherProperty objects on each Release.
You can chain as many of these as you like, for example:
.Include("Tracks.Artist").Include("AnotherProperty")
.Include("ThirdProperty.SomeItems").Where(r => r.something);
is perfectly valid. The only requirement is that you put the Include on the EntitySet, not on a query - you can't .Where().Include().
Don't worry about using include here
just do something like the following
var query =
from release in ctx.Releases
select new {
release,
originalTracks = from track in release.OriginalTracks
select new {
track,
releases = track.Releases,
orignialArtist = from artist in track.OriginalArtists
select new {
artist,
artist.OriginalTracks
}
}
}
var Releases = query.Select(x => x.Release);
Should load all of your data
I worked with information from this post here.
http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx
To include the nested entities without using string literals, use Select, like this:
context.Releases.Include(r => r.OriginalTracks.Select(t => t.OriginalArtist))
.Where(release => release.ReleaseID == id);

How to implement an IN clause in LinQ

I have two ILIst of these objects:
class ProductionMachineType
{
string code { get; set; }
IEnumerable<string> ProductionToolsLink { get; set; }
}
class ProductionTools
{
string code { get; set; }
}
I am looking for a fast Linq method that make me able to query the IList<ProductionMachineType> that contains at least one ProductionToolsLink contained inside the ILIst<ProductionTools>.
In SQL I would wite something like this:
SELECT
*
FROM
IList<ProductionMachineType>
WHERE
IList<ProductionMachineType>.ProductionToolsLink IN ILIst<ProductionTools>
Is there a way to do this?
Contains method can help you:
var names = new string[] { "Alex", "Colin", "Danny", "Diego" };
var matches = from person in people
where names.Contains(person.Firstname)
select person;
This will do it, but I can't guarantee how efficient it is...
var output = machines.Where(machine =>
machine.ProductionToolsLink
.Any(link => tools.Select(tool => tool.code).Contains(link)));

Resources