Is there a way to create a list of property values from a list of some type? - linq

Imagine that I have this class:
class Person
{
string Name { get; set; }
string Surname { get; set; }
string Other { get; set; }
}
I have a IList<Person> and now I want an IList<string> that is a sequence of the Name of each person.
I can use this loop:
var Names = new List<string>();
foreach (Person p in Persons)
{
Names.Add(p.Name);
}
But, I would like to know if linq (or any other option) allows me to do it more easily?
Thanks.

lstPersons.Select(x=> x.Name).ToList()

like this?
var Names = Persons.Select(p => p.Name).ToList();

var items = new List<Person>();
items.Add(...);
items.Add(...);
var people = items.Select(x=>x.name);

Related

RavenDB, Linq: Build an expression from a Property inside a nested hierarchy

I stored the objects of the following classes in a ravendb database:
public class Continent
{
public string Id { get; set; }
public string Name { get; set; }
public List<Country> Countries { get; set; }
}
public class Country
{
public string Name { get; set; }
public List<Province> Provinces { get; set; }
}
public class Province
{
public string Name { get; set; }
public List<City> Cities { get; set; }
}
public class City
{
public string Name { get; set; }
public string Address { get; set; }
}
I want to write a method that searches Continents having a specific city. The latter is a parameter of my method. In this method, I want to execute a dynamic query like the following: query= session.Query().Where(ConditionOnTheSearchedCity). But I can not formulate the Expression "ConditionOnTheSearchedCity" because I can not iterate till the Name of each City. In fact, to create an expression based on the Name of the City, I've tried the following which is not working:
ParameterExpression parameterExpression = Expression.Parameter(typeof(Continent), "p");
Expression NameExpression = Expression.PropertyOrField(
Expression.PropertyOrField(
Expression.PropertyOrField(
Expression.PropertyOrField(parameterExpression, "Countries"), "Provinces"),
"Cities"),
"Name");
Can you please help me? Thanks in advance
Why are you going to all the trouble of dynamically building Linq?
Why not just use the RavenDB's DocumentQuery API which allows you to easily build queries dynamically?
As I mentioned in the comments, the question is too broad, so I'll give you just a starting point by providing an example. For more information, take a look at How to: Use Expression Trees to Build Dynamic Queries.
Let have a parameter string cityName and want a filter like this
Expression<Func<Continent, bool>> filter = continent =>
continent.Countries.Any(country =>
country.Provinces.Any(province =>
province.Cities.Any(city => city.Name == cityName)));
It can be build dynamically like this
var city = Expression.Parameter(typeof(City), "city");
var cityCondition = Expression.Equal(Expression.PropertyOrField(city, "Name"), Expression.Constant(cityName));
var province = Expression.Parameter(typeof(Province), "province");
var provinceCondition = Expression.Call(
typeof(Enumerable), "Any", new[] { city.Type },
Expression.PropertyOrField(province, "Cities"),
Expression.Lambda(cityCondition, city));
var country = Expression.Parameter(typeof(Country), "country");
var countryCondition = Expression.Call(
typeof(Enumerable), "Any", new[] { province.Type },
Expression.PropertyOrField(country, "Provinces"),
Expression.Lambda(provinceCondition, province));
var continent = Expression.Parameter(typeof(Continent), "continent");
var continentCondition = Expression.Call(
typeof(Enumerable), "Any", new[] { country.Type },
Expression.PropertyOrField(continent, "Countries"),
Expression.Lambda(countryCondition, country));
var filter = Expression.Lambda<Func<Continent, bool>>(continentCondition, continent);

Dynamic Column Name in LinQ

I am having a class Item.
class Item{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public string Name { get; set; }
public string Description { get; set;}
}
I want to filter list of items based on dynamic column name.
Suppose I want list of Names then Column Name is "Name" and result will be list of names
If column name is Description, I need list of descriptions.
How to do this with LinQ?
Easy, just select the property you need from the list:
var items = new List<Item>();
//get names
var names = items.Select(x => x.Name);
//get descriptions
var descriptions = items.Select(x => x.Description);
Update:
You'll need a bit of reflection to do this:
var names = items.Select(x => x.GetType().GetProperty("Name").GetValue(x));
Throw this in a method for re-usability:
public IEnumerable<object> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x));
return values;
}
Of course this doesn't validate wether the column exists in the object. So it will throw a NullReferenceException when it doesn't. It returns an IEnumerable<object>, so you'll have to call ToString() on each object afterwards to get the value or call the ToString() in the query right after GetValue(x):
public IEnumerable<string> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x).ToString());
return values;
}
Usage:
var items = new List<Item>(); //fill it up
var result = GetColumn(items, "Name");

Select multiple columns in LINQ

I've written a LINQ query shown below :
List<Actions> actions = resourceActions.Actions.Select(s => s.ActionName).ToList();
How do I give for selecting multiple columns here ? ie I want to add columns s.ActionId and s.IsActive. I'm unable to apply it.
Make a class to represent the data you want:
public class ResourceAction
{
public int Id {get;set;}
public string Name {get; set; }
}
Select a list of those instead:
List<ResourceAction> actions = resourceActions.Actions
.Select(s => new ResourceAction() { Id = s.Id, Name = s.ActionName}).ToList();
I believe this is what your looking for. However you need to change the output to an anonymous type.
var actions = resourceActions.Actions.Select(s => new { s.ActionName, s.ActionId, s.IsActive } ).ToList();
You can use a anonymous type for this, for example
var actions = resourceActions.Actions.Select(s =>
new { Id = s.Id, Name = s.ActionName, Active = s.IsActive).ToList();
but a better way would be to create a class like
public class ActionWithId
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
List<ActionWithId> actions = resourceActions.Actions.Select(s =>
new ActionWithId() { Id = s.Id, Name = s.ActionName, Active = s.IsActive }).ToList();

How to use dynamic linq to query through nested arrays?

If I have class structure as follows:
private class A
{
int afoo { get; set; }
B[] bList { get; set; }
}
private class B
{
String bfoo { get; set; }
C[] cList { get; set; }
}
private class C
{
String cfoo { get; set; }
int cfoo2 { get; set; }
}
public class Master
{
A[] aList;
//qry for all where A.B.C.cfoo = "test"
}
How would I construct a dynamic LINQ statement to query for items in class C? Something like this
1) var qry = aList.blist.clist.AsQueryable().Where("cfoo = \"Test\"").Select();
My ultimate solution would be to pass the entire path in the dynamic part like this:
2) var qry = aList.AsQueryable().Where("bList.cList.cFoo = ""Test"").Select();
But from what I have tried you can not have the nested objects in the Where. So I am going to live with using templates to build the methods as in 1) above.
[Also, I am using the dynamic library from Scott Gu for the dynamic part.]
But, I can't get that to work. Any suggestions?
Thanks!
aList.SelectMany(a => a.bList)
.SelectMany(b => b.cList)
.Where(c => c.cfoo == "\"Test\"");
Try this:
aList.SelectMany(a => a.bList.SelectMany(b => b.cList))
.Where(c => c.cf002 == "Test")
.Select();

linq aggregate

class Category
{
public string Name { get; set; }
public int Count { get; set;}
}
Name Count
AA 2
BB 3
AA 4
I have an IEnumerable<Category>
and would like to get a list of Categories with unique names and the sum of multiple entries
Output
Name Count
AA 6
BB 3
Update
class Category
{
public string Name { get; set; }
public int CountA { get; set;}
public int CountB { get; set;}
public string Phone { get; set;}
}
How would I sum two columns. and the phone column can be the last row or any row
Your updated question isn't entirely clear in terms of the phone number, but I suspect you want something like:
var query = from category in list
group category by category.Name into grouped
select new { Name = grouped.Key,
SumA = grouped.Sum(x => x.CountA),
SumB = grouped.Sum(x => x.CountB),
Phone = grouped.Last().Phone };
Changing grouped.Last() to grouped.First() would be more efficient, by the way.
Evaluating multiple aggregates in this way isn't terribly efficient in general. The Push LINQ project developed by myself and Marc Gravell makes it a lot more efficient at the cost of not being quite as easy to use. You might want to look into it if you need to deal with a lot of data.
var foo = new List<Category>() {
new Category() { Name = "AA", Count = 2},
new Category() { Name = "BB", Count = 3},
new Category() { Name = "AA", Count = 4}
};
var bar = foo.GroupBy(c => c.Name).Select(g => new Category(){ Name = g.Key, Count = g.Sum(c => c.Count) });

Resources