I have started experimenting a bit with the LINQ DynamicLibrary. I am trying to replace a bunch of LINQ statements with a few or just one Dynamic LINQ query. My existing static query is as follows:
private List<TicketChartData> GenerateImpl(IList<ServiceItem> myList)
{
var output = from ticket in myList
group ticket by ticket.Region into grouped
orderby grouped.Count() descending
select new TicketChartData(grouped.Key, grouped.Count(), grouped.ToList());
return output.ToList();
}
As you see, my unit of work is ServiceItem.
This works all fine and gets me a result set grouped by Region. Using DynamicLibrary my attempt is to be able to group by any valid dynamic field (I will handle any validations separately). So, I tried writing the same query using DynamicLibrary, but am not very successful. Here is the new method which of course doesn't compile:
private List<TicketChartData> GenerateImpl(IList<ServiceItem> myList, string field)
{
IQueryable<ServiceItem> queryableList = myList.AsQueryable<ServiceItem>();
IQueryable groupedList = queryableList.GroupBy(field,"it").
OrderBy("Key descending").
Select("new (Key as Key)"); // Not what i need
return output.ToList();
}
I have been unable to extract neither the Count nor the List from the grouping. How do I do this? I have spent considerable amount of time looking for a solution. If possible I want to be able to avoid using Reflection. I have had some pointers in this front at the following link, but it doesn't help my actual problem. Thanks in advance for any help.
System.LINQ.Dynamic: Select(" new (...)") into a List<T> (or any other enumerable collection of <T>)
For Count it can be written as following:
private List GenerateImpl(IList myList, string field)
{
IQueryable queryableList = myList.AsQueryable();
IQueryable groupedList = queryableList.GroupBy(field,"it").
OrderBy("Key descending").
Select("new (Key as Key, Count() as Count)");
// Note: IQueryable doesn't have ToList() implementation - only IEnumerable
return output.ToList(); // will not work
}
For List - possible you'll need to add your custom implementation to DynamicLibrary...
See how it was done for Contains() method here
A solution is to use the .QueryByCube LINQ extension method provided by my product AdaptiveLINQ.
Disclaimer: I'm the AdaptiveLINQ developer
Related
I'm trying to add an extra parameter to a list of ef objects to track processing, but I keep running into having to initialize each list item explicitly. What's the correct linq way to do this? Aside from terseness, is there any advantage to a linq syntax in this case?
List<app_subjects> subjectList = AppMySQLQueries.GetAllSubjects();
List<Tuple<app_subjects, bool>> subjectCollection = new List<Tuple<app_subjects, bool>>(subjectList.Count);
foreach (app_subjects subject in subjectList)
{
subjectCollection.Add(Tuple.Create(subject, false));
}
I have searched the site without success.
You just want to use a projection here ( Select ) which applies the transformation in your lambda expression to each element in the source collection.
List<Tuple<app_subjects, bool>> tuples = subjectList.Select(x => new Tuple<app_subjects, bool>(x, false)).ToList();
The ToList() call is not entirely necessary, if you removed it then the method will return an IEnumerable<Tuple<app_subjects, bool>>. If you're just going to iterate the collection of tuples afterwards the ToList call should be removed as it forces execution (enumerates the IEnumberable) and then your next operation (the foreach) would do the same, making the code perform worse.
Like this?
subjectList.Select(s => Tuple.Create(s, false)).ToList();
With C# 10.0 (.NET 6.0) this is even easier and cleaner. Along with named tuples we can also declare a tuple by simply putting the values in round brackets.
List<(string NamedProperty1, int NamedProperty2)> _tuples = new();
_tuples = _objectList.Select(o => (o.SomeProperty1, o.SomeProperty2)).ToList();
try this.
List<Tuple<app_subjects, bool>> subjectCollection = subjectList.CovertAll( subject => new Tuple<app_subjects, bool>(){
subject,
false
}).ToList();
I have an object that contains a list of child objects, each of which in turn contains a list of children, and so on. Using that first generation of children only, I want to combine all those lists as cleanly and cheaply as possible. I know I can do something like
public List<T> UnifiedListOfTChildren<T>()
{
List<T> newlist = new List<T>();
foreach (childThing in myChildren)
{
newlist = newlist.Concat<T>(childThing.TChildren);
}
return newlist;
}
but is there a more elegant, less expensive LINQ method I'm missing?
EDIT If you've landed at this question the same way I did and are new to SelectMany, I strongly recommend this visual explanation of how to use it. Comes up near the top in google results currently, but is worth skipping straight to.
var newList = myChildren.SelectMany(c => c.TChildren);
Is there a way to remove all items except first one from any type of collection (Control.Items, List ....) using LINQ only ?
No. LINQ is designed for querying collections (no side-effects), not for adding or removing items.
What you can do is write a query that takes the first element of the collection:
var result = source.Take(1);
Note that LINQ doesn't work with all types of collections; you need a LINQ provider to make LINQ work. For instance, source must implement IEnumerable<T> to use the extension methods of the Enumerable Class (LINQ-to-Objects).
How about something using reflection?
static void RemoveButFirst(object o){
Type t = o.GetType();
System.Reflection.MethodInfo rm = t.GetMethod("RemoveAt",
new Type[]{typeof(int)});
System.Reflection.PropertyInfo count = t.GetProperty("Count");
for (int n = (int)(count.GetValue(o,null)) ; n>1; n--)
rm.Invoke(o, new object[]{n-1});
}
This would work any time your collection exposed an int Count property and a RemoveAt(int) method, which I think those collections should.
And a more concise version, using dynamic, if you work with C# 4.0:
public static void RemoveBut(dynamic col, int k){
for (int n = col.Count; n>k; n--)
col.RemoveAt(n-1);
}
You can use .Take(1), but it returns a new collection, and leaves the original intact.
The idea of LINQ came from functional programming where everything is immutable, because of that, they didn't make it possible to modify the collections with LINQ.
Jon Skeet has a comment on the subject: LINQ equivalent of foreach for IEnumerable<T>
How about (in linq):
var result = list.Where(l => l != list.First());
But this would be better:
var result = list.Take(1);
List<string> collection = new List<string>();
collection.RemoveAll(p => p.StartsWith("something"));
listXpto.Where(x=>true /* here goes your query */)
.Select(x=>{listXpto.Remove(x); return null})
But I donĀ“t know the real utility of that.
Remember that the remove method is for ILists, not IQueryable in general.
I have an array that stores the order that I want to sort a list by.
SortOrderArray: "Color", "Volume", "Weight"
So I want to order my list by Color, Volume, then Weight
MyList.OrderBy(a=>a.Color).ThenBy(a=>a.Volume).ThenBy(a=>a.Weight).ToList();
So that's pretty good. Now, I want to be able to write a function that does this sorting based on the sortOrder array I send in:
public List<row> GetSortedList(List<row> list, string[] sortOrder){
???
}
I can't figure out how to do this without writing a linq query for every combination of sortOrders (27 different queries just seems like the worst way to accomplish this, and has a fairly high possibility of me making a tiny mistake). I would like to be able to just write 3 linq queries that reorders the list according to each of the 3 sorting methods, something like this:
switch(sortOrder[0]){
Sort by the first sort method
}
switch(sortOrder[1]){
Sort by the second sort method
}
switch(sortOrder[2]){
Sort by the third sort method
}
But if I try doing the above code, it just resorts it each time, instead of doing sub-sorts after the one above it. Hope that is clear, any help would be appreciated.
Two things. You need a sort method that performs a "stable sort" - it keeps the existing order of items with identical keys. And then you need to call it in reverse order of your sort criteria , so that the primary sort is the last one you do.
If you have a limited number of possible fields to sort by, a switch might be the best solution. If you're looking for something that scales, you'll have to generate a lambda expression on the fly, and use reflection to call the appropriately-typed .OrderBy and .ThenBy methods.
Using Dynamic LINQ, you can do something like:
public List<row> GetSortedList(List<row> list, string[] sortOrder)
{
// argument-validation, including testing that
// sort-order has at least 1 item.
return sortOrder.Skip(1)
.Aggregate(list.AsQueryable().OrderBy(sortOrder.First()),
(query, nextSortTerm) => query.ThenBy(nextSortTerm))
.ToList();
}
Essentially: OrderBy the first sort-term, ThenBy the remaining.
EDIT: Added an AsQueryable call to make Dynamic LINQ work on IEnumerable<T>
Have a look at this post from Scott Guthrie: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I guess, you are not using the return value of the order clauses.
public List<row> GetSortedList(List<row> list, string[] sortOrder)
{
IOrderedEnumerable<row> result = null;
bool first = true;
foreach(sortClause in sortOrder)
{
switch sortClause
{
case "Color":
if(first)
result = list.OrderBy(x => x.Color);
else
result = result.ThenBy(x => x.Color);
break;
// the other cases
}
first = false;
}
return result.ToList();
}
Something like that.
I have a collection of anonymous class and I want to return an empty list of it.
What is the best readable expression to use?
I though of the following but I don't think they are readably enough:
var result = MyCollection.Take(0).ToList();
var result = MyCollection.Where(p => false).ToList();
Note: I don't want to empty the collection itself.
Any suggestion!
Whats about:
Enumerable.Empty<T>();
This returns an empty enumerable which is of type T. If you really want a List so you are free to do this:
Enumerable.Empty<T>().ToList<T>();
Actually, if you use a generic extension you don't even have to use any Linq to achieve this, you already have the anonymous type exposed through T
public static IList<T> GetEmptyList<T>(this IEnumerable<T> source)
{
return new List<T>();
}
var emp = MyCollection.GetEmptyList();
Given that your first suggestion works and should perform well - if readability is the only issue, why not create an extension method:
public static IList<T> CreateEmptyCopy(this IEnumerable<T> source)
{
return source.Take(0).ToList();
}
Now you can refactor your example to
var result = MyCollection.CreateEmptyCopy();
For performance reasons, you should stick with the first option you came up with.
The other one would iterate over the entire collection before returning an empty list.
Because the anonymous type there is no way, in source code, to create a list. There is, however, a way to create such list through reflection.