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
Related
I have a list of Cutdetails. I am trying to write a function using LINQ that will return the count of bars in the list where the CODE , BRAND, CODE and LENGTH all match. I want to be able to specify all these parameters and return a number for the number of matches.
I have tried using foreach statements which is fine but i'm sure there is an neater and smarter way to do it using LINQ. Any suggestions?
List<Bar> bars = new List<Bar>();
public class Bar
{
public string Brand { set; get; }
public string System { set; get; }
public string Code { set; get; }
public string Length { set; get; }
}
Thanks in advance!
Will
You can filter using the match and then do a count.
var occurences = bars.Where(x => x.Brand == "Brand" && x.Code == "code").Count();
How do I created the appropriate AbstractIndexCreationTask for the following scenario?
For a scenario of multiple blogs, how do I get the tags from specific blogs and the tag-count for these?
Members of interest for data-structure stored in RavenDB:
public class BlogPost {
public string BlogKey { get; set; }
public IEnumerable<string> Tags { get; set; }
/* ... */
}
The method I need to implement has the following signature:
public Dictionary<string, int> GetTagsByBlogs(string tag, params string[] blogKeys)
In normal LINQ I would write something like this:
var tags = from post in blogPosts
from tag in post.Tags
where blogKeys.Contains(post.BlogKey)
group tag by tag into g
select new {
Tag = g.Key,
Count = g.Count(),
};
But neither SelectMany or GroupBy are supported in RavenDB. I've tried different combinations for a map-reduce solution, but I can't figure out how to do this since the map and the reduce differ in data-structure.
How to create a tag cloud is described in the knowledge base of RavenDB.
In your case, you have to include the BlogKey in the index, especially in the group by clause:
public class Tags_Count : AbstractIndexCreationTask<BlogPost, Tags_Count.ReduceResult>
{
public class ReduceResult
{
public string BlogKey { get; set; }
public string Name { get; set; }
public int Count { get; set; }
}
public Tags_Count()
{
Map = posts => from post in posts
from tag in post.Tags
select new {
BlogKey = post.BlogKey,
Name = tag.ToString().ToLower(),
Count = 1
};
Reduce = results => from tagCount in results
group tagCount by new {
tagCount.BlogKey,
tagCount.Name } into g
select new {
BlogKey = g.Key.BlogKey,
Name = g.Key.Name,
Count = g.Sum(x => x.Count)
};
Sort(result => result.Count, SortOptions.Int);
}
}
Then query that index with the desired BlogKey:
var result = session.Query<Tags_Count.ReduceResult, Tags_Count>()
.Where(x => x.BlogKey = myBlogKey)
.OrderByDescending(x => x.Count)
.ToArray();
If you need to query for multiple blogs, you can try this query:
var tagsByBlogs = session.Query<Tags_Count.ReduceResult, Tags_Count>()
.Where(x => x.BlogKey.In<string>(blogKeys))
.OrderByDescending(x => x.Count)
.ToArray();
AFAIK that is as far as you can get with an index. You still have to aggregate the results on the client side as you did in your original question, except that you can skip the filtering on blogKeys:
var tags = from tag in tagsByBlogs
group tag by Name into g
select new {
Tag = g.Key,
Count = g.Count(),
};
Take a look at faceted search, you can specify the criteria at query time, like so:
var facetResults = s.Query<BlogPost>("BlogIndex")
.Where(x => x.BlogKey == "1" || x.BlogKey == "5" ...)
.ToFacets("facets/BlogFacets");
Then the grouping (and counts) is done on all the results that match the where clause.
Update You'll need an index that looks something like this:
from post in blogPosts
from tag in post.Tags
select new
{
post.BlogKey
Tag = tag
}
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)
};
Given the follow data class,
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
}
how can I get a sub-query into the ItemNames field?
My current query of
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
};
doesn't seem to be doing the trick...
The data structure is
MenuHeaders MenuItems
----------- ---------
ID ID
HeaderName <-(FK)--MenuHeaderID
MenuItemName
I ended up just changing from a List to IEnumerable. This fixed it.
Wouldnt you want to just put a where in your sub-select to filter that down to all the menu items with the MenuHeaderID equals mh.HeaderName. You can just .Equals() with the StringComparison type if you want as well.
Here is an example...
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName where mhi.MenuHeaderID = mh.HeaderName).ToList<string>()
};
My guess is that your not initiliazing the list within your class. I basing this off the experience I was having with Nhibernate.
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
public EmployeeMenu()
{
ItemNames=new List<string>();
}
}
Hope this helps.
Okay. Try replacing
(from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
by
mh.MenuItems
.AsEnumerable()
.Select(mhi => mhi.MenuItemName)
.ToList()
I question if you want a where clause in there somewhere, but this should get you past the runtime exception.
Any time you see an error message of the form "LINQ to Entities does recognize the method ... and this method can not be translated into a store expression" LINQ to Entities is telling you that it can't figure out how to translate part of the expression tree into a SQL statement. This means you need to pull things client side so that LINQ to Entities doesn't try to translate something that it can't translate.
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)));