Reputation: 341
From Efficient data structure to hold employee's activities? , i have a List of type ActividadEmpleado which is declared as:
public string Empleado { get; set; }
public DateTime Fecha { get; set; }
public string Actividad { get; set; }
The LINQ query variable reorders the result in the way i need, which is to store by date and then by ActividadEmpleado and a string. However, var types cannot be passed to methods, so searching this site i am finding out that i either need to create a class to store the results or to modify the LINQ variable to return a List, but i am having issues with the proper declaration.
The LINQ variable is:
var queryActividades = listaActividad
.GroupBy(a => a.Fecha, (fecha, fechaActividades) => new
{
Fecha = fecha,
FechaActividades = fechaActividades
.GroupBy(a => a.Empleado, (nombreEmpleado, actividadesEmpleado) => new
{
Actividades = actividadesEmpleado,
NombreEmpleado = nombreEmpleado
})
.OrderBy(a => a.NombreEmpleado)
})
.OrderBy(a => a.Fecha);
Visual Studio says that queryActividades is:
IOrderedEnumerable<'a>
Anonymous Types:
'a new datetime fecha, iorderedenumerable<'b>
'b new IEnumerable<ActividadEmpleado> Actividades, string NombreEmpleado
I need to pass queryActividades to another method. I tried passing it as an Object but then i lose the extension methods such as Where<> (can i cast it somehow?)
I also read that declaring the results as a tuple should work, but i think declaring a new class is cleaner.
I am just starting with LINQ, i have avoided it to use regular data structures but in this case it's really helpful and would like to know how to either handle anonymous types in them or convert the result to a regular List
Final solution:
class GrupoActividad
{
public DateTime Fecha { get; set; }
public IEnumerable<Actividad> Actividades { get; set; }
}
class Actividad
{
public IEnumerable<ActividadEmpleado> Actividades { get; set; }
public string NombreEmpleado { get; set; }
}
var queryActividades = listaActividad
.GroupBy(a => a.Fecha, (fecha, fechaActividades) => new GrupoActividad
{
Fecha = fecha,
Actividades = fechaActividades
.GroupBy(a => a.Empleado, (nombreEmpleado, actividadesEmpleado) => new Actividad
{
Actividades = actividadesEmpleado,
NombreEmpleado = nombreEmpleado
})
.OrderBy(a => a.NombreEmpleado)
})
.OrderBy(a => a.Fecha);
Receiving method:
var actividades = from a in queryActividades
where a.Fecha == fechaCiclo
select new { a.Fecha, a.Actividades };
foreach (var item in actividades)
{
//cycle that holds each day's activities
foreach (var empleado in item.Actividades)
{
//cycle that holds each employee with activities in that day
foreach (var actividad in empleado.Actividades)
{
//final cycle that actually reads the activity
ActividadEmpleado actividadEmpleado = (ActividadEmpleado)actividad;
}
}
}
Upvotes: 2
Views: 2903
Reputation: 205539
You can use the approach presented by D Stanley. But it would be kind a boring to have to create such classes for any similar query that you write in the future. Instead, you can introduce as generic class for that, like this
public class Grouping<TKey, TElement>
{
public TKey Key { get; set; }
public IEnumerable<TElement> Elements { get; set; }
}
and use it instead of the anonymous types like this
var queryActividades = listaActividad
.GroupBy(a => a.Fecha, (fecha, fechaActividades) => new Grouping<DateTime, Grouping<string, ActividadEmpleado>>
{
Key = fecha,
Elements = fechaActividades
.GroupBy(a => a.Empleado, (nombreEmpleado, actividadesEmpleado) => new Grouping<string, ActividadEmpleado>
{
Key = nombreEmpleado,
Elements = actividadesEmpleado
})
.OrderBy(a => a.Key)
})
.OrderBy(a => a.Key);
which can be be passed as IEnumerable<Grouping<DateTime, Grouping<string, ActividadEmpleado>>>
.
As you can see, there is a trade off between reusability and readability. Basically this is a Tuple
with a little more meaningful names. Note that although we cannot improve the verbosity in the result, we can use a similar technique to Tuple.Create
to remove the verbosity inside the query, by adding a class like this
public static class Grouping
{
public static Grouping<TKey, TElement> Create<TKey, TElement>(TKey key, IEnumerable<TElement> elements)
{
return new Grouping<TKey, TElement> { Key = key, Elements = elements };
}
}
and use it like this
var queryActividades = listaActividad
.GroupBy(a => a.Fecha, (fecha, fechaActividades) => Grouping.Create(
fecha, fechaActividades
.GroupBy(a => a.Empleado, (nombreEmpleado, actividadesEmpleado) => Grouping.Create(
nombreEmpleado, actividadesEmpleado))
.OrderBy(a => a.Key)))
.OrderBy(a => a.Key);
Upvotes: 2
Reputation: 152501
Right now you are creating a collection that's based on an anonymous type (actually two anonymous types), which cannot practically be passed to another method (other than by using reflection or dynamic
). The cleanest way is to create a concrete type that represents the collection - something like
public class ActivityGroup
{
public DateTime Fecha {get; set;}
public IEnumerable<Activity> Activities {get; set;}
}
public class Activity
{
public IEnumerable<Activity> Actividades {get; set;}
public string NombreEmpleado {get; set;}
}
then change your query to:
var queryActividades = listaActividad
.GroupBy(a => a.Fecha, (fecha, fechaActividades) => new ActivityGroup
{
Fecha = fecha,
FechaActividades = fechaActividades
.GroupBy(a => a.Empleado, (nombreEmpleado, actividadesEmpleado) => new Activity
{
Actividades = actividadesEmpleado,
NombreEmpleado = nombreEmpleado
})
.OrderBy(a => a.NombreEmpleado)
})
.OrderBy(a => a.Fecha);
and pass it as an IEnumerable<ActivityGroup>
Upvotes: 5
Reputation: 292355
You can't pass anonymous types between methods; if you need to pass the data to another method, you need to create an explicit named type that contains the Actividades
and NombreEmpleado
properties, and another one with Fecha
and FechaActividades
.
Upvotes: 0