I've a List that contains a records below:
name:apple
category:fruit
name:choysum
category:vegetable
name:chicken
category: poultry
name: lamb
category: meat
...
I need to order this list by category below:
vegetable
fruit
poultry
meat..
Could someone please help me how above is possible using LINQ. I'm thinking of creating a another property called ordinal and tag as 1,2,3,4 for above categories. Then order by ordinal.
Yes, you could do something like:
var orderedCategories = new List<string> { "fruit", "vegetable", "poulty", "meat" };
var ordered = list.OrderBy(x => orderedCategories.IndexOf(x.Category))
.ToList();
If you have lot of categories, you might want to use a Dictionary<string, int> (or a Dictionary<Category, int> if you have a separate class for categories).
Related
I am just not understanding the LINQ non-query syntax for GroupBy.
I have a collection of objects that I want to group by a single property. In this case Name
{ Id="1", Name="Bob", Age="23" }
{ Id="2", Name="Sally", Age="41" }
{ Id="3", Name="Bob", Age="73" }
{ Id="4", Name="Bob", Age="34" }
I would like to end up with a collection of all the unique names
{ Name="Bob" }
{ Name="Sally" }
Based on some examples I looked at I thought this would be the way to do it
var uniqueNameCollection = Persons.GroupBy(x => x.Name).Select(y => y.Key).ToList();
But I ended up with a collection with one item. So I though maybe I was over complicating things with the projection. I tried this
var uniqueNameCollection = Persons.GroupBy(x => x.Name).ToList();
Same result. I ended up with a single item in the collection. What am I doing wrong here? I am just looking to GroupBy the Name property.
var names = Persons.Select(p => p.Name).Distinct().ToList()
If you just want names
LINQ's GroupBy doesn't work the same way that SQL's GROUP BY does.
GroupBy takes a sequence and a function to find the field to group by as parameters, and return a sequence of IGroupings that each have a Key that is the field value that was grouped by and sequence of elements in that group.
IEnumerable<IGrouping<TSource>> GroupBy<TSource, TKey>(
IEnumerable<TSource> sequence,
Func<TSource, TKey> keySelector)
{ ... }
So if you start with a list like this:
class Person
{
public string Name;
}
var people = new List<Person> {
new Person { Name = "Adam" },
new Person { Name = "Eve" }
}
Grouping by name will look like this
IEnumerable<IGrouping<Person>> groups = people.GroupBy(person => person.Name);
You could then select the key from each group like this:
IEnumerable<string> names = groups.Select(group => group.Key);
names will be distinct because if there were multiple people with the same name, they would have been in the same group and there would only be one group with that name.
For what you need, it would probably be more efficient to just select the names and then use Distinct
var names = people.Select(p => p.Name).Distinct();
var uniqueNameCollection = Persons.GroupBy(x => x.Name).Select(y => y.Key).ToList();
Appears valid to me. .net Fiddle showing proper expected outcome: https://dotnetfiddle.net/2hqOvt
Using your data I ran the following code statement
var uniqueNameCollection = people.GroupBy(x => x.Name).Select(y => y.Key).ToList();
The return results were List
Bob
Sally
With 2 items in the List
run the following statement and your count should be 2.
people.GroupBy(x => x.Name).Select(y => y.Key).ToList().Count();
Works for me, download a nugget MoreLinq
using MoreLinq
var distinctitems = list.DistinctBy( u => u.Name);
I have a generic list of assets (List<Asset>) and one of the assets properties is called Tags which is a list of strings
How would I do a Linq query to get a distinct list of tags. I tried
assetList.Select(a => a.Tags).Distinct()
but this returns me an IEnumerable<List<string>> instead of an IEnumerable<string>
You was close. You need to use Enumerable.SelectMany to select all tags and flatten them into one sequence:
assetList.SelectMany(a => a.Tags).Distinct()
assetList.SelecMany(a => a.Tags).Distinct() . correct it
more information about differences Difference Between Select and SelectMany
Select only takes objects as they are and Tags is a list, so it takes lists. If you need items from these lists, you have to flatten them into one list and then proceed with other operations.
assetList.SelectMany(a => a.Tags).Distinct();
A nice example from MSDN on SelectMany
PetOwner[] petOwners =
{ new PetOwner { Name="Higa, Sidney",
Pets = new List<string>{ "Scruffy", "Sam" } },
new PetOwner { Name="Ashkenazi, Ronen",
Pets = new List<string>{ "Walker", "Sugar" } },
new PetOwner { Name="Price, Vernette",
Pets = new List<string>{ "Scratches", "Diesel" } } };
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
produces the following list
Scruffy, Sam, Walker, Sugar, Scratches, Diesel
Is it possible to create a dynamic selector like the below one in a simple way, and how?
Func<Company, string> companyName = x.CompanyName;
Func<Company, int> companyId = x.CompanyId;
var result = datacontext.Select(x => new
{
CompanyName = companyName,
CompanyId = companyId
});
The above code throws exception: "Unable to create a constant value of type 'System.Func`2... ...Only primitive types or enumeration types are supported in this context."
The problem is that I need to dynamically select up to 8 fields out of possible 50 from approximately 10 different tables, and these fields can be of types string, int, datetime nullable and not nullable. It is quiet difficult to dynamically construct a selector with Expressions. What is the best way to tackle this?
var result = datacontext.Select(x => new
{
CompanyName = mcname(x),
CompanyId = companyId(x)
});
But where is the reason?
Your Funcs should look like this:
Func<Company, string> companyName = (company => company.CompanyName);
Func<Company, int> companyId = (company => company.CompanyId);
To use your func:
var result = datacontext.Select(x => new
{
CompanyName = companyName(x),
CompanyId = companyId(x)
});
I don't know of a way to dynamically create a query for specific fields. You can dynamically chain filters with a single execution though... Unless you're storing a significant amount of information on each row or are loading hundreds of thousands of rows I wouldn't worry about it.
P.S. You'll want to be careful using custom functions in your primary database filters. LINQ can't translate all commands to a native SQL query so it may end up pulling an entire table and filtering in within your code. Just pay attention.
I'm not exactly sure what you are trying to accomplish, but could what you want be done simply like this?
var result = datacontext.Select(x => new
{
CompanyName = x.companyName,
CompanyId = x.companyId
});
OK, another LINQ question. How do I do an "IN" condition using LINQ. I have an IEnumerable list of myObject and want to do something like myObject.Description in('Help', 'Admin', 'Docs'). How can I accomplish this? Thanks
IN in sql is equivalent is Contains in LINQ
string[] countries = new string[] { "UK", "USA", "Australia" };
var customers =
from c in context.Customers
where countries.Contains(c.Country)
select c;
Use Contains on a collection:
string[] descriptions = { "Help", "Admin", "Docs" };
var query = from foo in list
where descriptions.Contains(foo.Description)
select ...;
(For larger collections, a HashSet<T> might be better.)
I have a Dictionary<int, int> idsAndTypes = new Dictionary<int, int>(); and i have a
List<Product> products = new List<Product>()
as list of products , the product class is as below
class Product
{
public int Id {get;set;}
public int Type{get;set;}
}
the dictionary idsAndTypes contains id's and types , now i want to use a linq query on the list to update the type of products based on id's in the dictionary....
i know the other way can be like following :
foreach (int item in idsAndTypes.Keys)
{
Product.Where(product => product.Id == item).
Select(product => product).ToList()[0].
Type = idsAndTypes[item];
}
but i want to do it with a linq query to avoid the foreach loop, is there a way to do it ?
Please suggest...
Well, LINQ is really for querying not for updating.
There may be a way of doing it without the loop, but I think the loop is the cleanest way. However, I wouldn't use that loop. You've got something which is very quick to look up, and you're just iterating through it... but then doing a lookup (effectively) on a slow data structure in terms of lookup. I'd do this:
foreach (Product p in products)
{
int type;
if (idsAndTypes.TryGetValue(product.Id, out type))
{
p.LinkedProductType = type;
}
}
One difference here - that will update all the products in the list with values in the dictionary; your current code will only do the first product in the list with the given ID. I hope that isn't a problem.
Your sample code is quite confusing. But I think what you want is:
products = products.Select(p =>
{
p.LinkedProductType = idAndTypes[p.ID];
return p;
}
);
While this should achieve the goal, I would considered it an abuse of LINQ.