I'm using the GroupByMany extension method illustrated here:
http://blogs.msdn.com/b/mitsu/archive/2007/12/22/playing-with-linq-grouping-groupbymany.aspx
This is working great, except I'm trying to group based on a list of entity properties.
An example of this would be a Car class that has a set of properties:
public class Car
{
private string Model {get; set;}
private int Year {get; set;}
private List<CarProperty> Properties { get; set; }
}
public class CarProperty
{
private string Name { get; set; }
private string Value { get; set; }
}
If I have a list of Car objects and the respective properties. The main issue is that the class can contain multiple properties with the same name:
Car1 : {"Colour", "Red"}, {"Colour", "Blue"}
Car2 : {"Colour", "Blue"}
Car3 : {"Colour", "Black"}, {"Colour", "Red"}
Car4 : {"Colour", "Yellow"}
I can create group selectors to group by 'Year' or 'Model' very easily:
c => c.Model;
c => c.Year;
The problem is I'm trying to create a group selector for one of the properties.
I have tried this (to group by car colour):
c => c.Properties.Where(p => p.Name == "Colour").Select(p => p.Value));
But it's create a composite grouping key. I somehow need to flatten the list of possible grouping values to give:
Red: Car1, Car3
Blue: Car1, Car2
Black: Car3
Yellow: Car4
EDIT:
If the GroupByMany isn't the way to go, I'd appreciate any suggestions to take a list of groupings like {"Year", "Model", "Colour"} which will multi-level group the cars in the same order as the list of groups.
Year
--> Model
--> Colour
It seems I was making the whole thing more complicated than necessary. This answer helped me find a solution:
LINQ: grouping based on property in sublist
Related
how can I get a new list of object using lambda from the below scenario:
Example class:
AllCARS is a collection:
public class AllCars
{
int id {get; set;}
List cars {get; set;}
}
public class cars
{
string color;
string model;
}
I tried the following which didn't produce all rows:
var carRepo = new AllCars{};
var carsWithIds = carRepo.select(a => new {a.id, a.cars.color, a.cars.model})
Here is an example of my tables:
AllCars = id:123
cars[]
color model
red honda
blue toyota
How can i write the lambda function to produce a list like this
123 red honda
123 blue toyota
If I understand the question right, you cannot perform a linq request to an object with collection. However, you can query the collection inside this object to produce the required output. Hope the following code snippet would help:
var carsWithIds = carRepo.cars.Select(a => new {id = carRepo.id, a.color, a.model });
This code allows you to get a new object of anonymous class, that contains fields: id, color and model.
There is a little inaccuracy in your code example. Do not forget to make properties of AllCars and cars classes public; otherwise they won't be accessible.
Let AllCarsList be the collection of AllCars then you can make use of extension method .SelectMany to get the resultCollection. Please go through the following lines of code and the example and please let me know if it helps as well as for more clarifications.
var resltCollection = AllCarsList.SelectMany(p => p.cars,(parent, child) => new
{
ID = parent.id,
Color = child.color,
Model = child.model
}).ToList();
Here is a working Example
I want to sort properties based off an attribute parameter Order that is given.
Attribute:
public class Items: Attribute
{
public int Order { get; set; }
public Items(int order)
{
this.Order = order;
}
}
Implementation:
public class client
{
[Items(1)]
public string fName {get; set;}
[Items(3)]
public string lName {get; set;}
[Items(2)]
public string mName {get; set;}
}
Get all Properties:
var properties = typeof(client).GetProperties().Where(
prop => prop.IsDefined(typeof(Items), false));
Sort by Order#?
This is what I tried but it does not work
Array.Sort(properties, delegate (Items x, Items y)
{ return x.Order.CompareTo(y.Order); });
How do I sort the properties based off the Order?
This has been solved, but would like to extend the question.
Is there a way to sort properties without having to put an "Order" on them. I am wanting to just have an attribute "EndOfList" Or "Last" that would state be sure to sort these last. So that I would not have to clutter up the code with Orders.
You can use Linq and OrderBy
var sorted = properties
.OrderBy(p => ((Items)p.GetCustomAttributes(true)
.FirstOrDefault(a => a is Items)).Order);
This results in following order: fName, mName, lName.
So what happens inside the OrderBy: access custom properties. Find a first that is of type Items and use the Order property as a sort parameter.
To sort in reversed order just use OrderByDescending.
You can try this
var properties = typeof(client).GetProperties().Where(prop => prop.IsDefined(typeof(Items), false));
var sortedProperties = properties.OrderBy(x => ((Items)x.GetCustomAttributes(typeof(Items), false).FirstOrDefault()).Order);
I have a web application using Entity Framework code first and I want to be able to hook up my example data below into various Entity data aware controls in ASP.NET.
By using the ItemType property of the control to specify my custom cencapsulation class and the SelectMethod property of the control to call the LINQ queryI need to return an IQueryable collection which the control can use and work with automatic pagination etc.
Lets assume we have an entity to start with :-
public class MyEntity
{
[Key]
public int MyObjectId { get; set; }
public string Name { get; set;}
}
So this LINQ query will work fine
public IQueryable<MyObject> GetMyObjects()
{
SiteContext db = new SiteContext();
var myObjects = (from m in db.MyObjects
select m);
return myObjects;
}
Now using the following sample data :-
MyObjectId Name
1 Apple
2 Orange
3 Apple
4 Apple
5 Pear
6 Orange
7 Apple
8 Grapes
9 Apple
10 Orange
What I want to do is group the data and provide a count like this:-
Name Count
Apple 5
Orange 3
Pear 1
Grapes 1
So my first step is to create a class to encapsulate the Entity and provide the additional count column :-
public class MyObjectWithCount
{
public MyObject MyObject { get; set; }
public int Count { get; set; }
}
Now I want to get some results with a LINQ query
public IQueryable<MyObjectWithCount> GetMyObjectsWithCount()
{
SiteContext db = new SiteContext();
var myObjects = (from m in db.MyObjects
group m by m.Name into grouped
select new MyObjectWithCount()
{
MyObject = grouped,
Count = ?what goes here?
});
return myObjects;
}
So I have 2 problems with my select new MyObjectWithCount()
1) How do I get the count for each grouped item
2) How do I convert grouped into Myobject. As it stands, I get the design time error of cannot implicitly convert type system.linq.grouping to MyObject.
If I did not perform grouping in the LINQ query, then I am able to successfully return an IQueryable to my control and it all hooks up fine (but obviously the count is missing at this stage)
Also to clarify, this is an oversimplied example of my Entity/class, I do need to have access to the full MyObject entity as in the real world there will be many fields, and not just a name field as in the example above.
Thanks everyone for any help you can offer
You can use this linq query:
var myObjects = (from m in db.MyObjects
group m by m.Name into grouped
select new MyObjectWithCount()
{
MyObject = grouped.FirstOrDefault(),
Count = grouped.Count()
});
In this case your MyObjectProperty will have MyObjectId and other properties of first element in group.
If you have same other then MyObjectId properties for all objects with same Name, you should group by all properties excluding MyObjectId:
var myObjects = (from m in db.MyObjects
group m by new {m.Name, m.AnyOtherProperty, ...} into grouped
select new MyObjectWithCount()
{
MyObject = grouped.FirstOrDefault(),
Count = grouped.Count()
});
It will protect you from collapsing of group of different objects in one object.
Your Select is slightly off.
Also, as your return type is IQueryable<MyObjectWithCount> add AsQueryable.
EDIT: From your comments, you want to return the items in your class too, in that case you'd have to do the following:
Add another property to MyObjectWithCount to hold the matching properties and a string to contain the Name searched on:
public string Name { get; set; }
public List<MyObject> MatchingObjects { get; set; }
Then your Count can just work off that:
public int Count
{
get
{
return MatchingObjects.Count;
}
}
Then your LINQ will be:
var myObjects = (from m in db.MyObjects
group m by m.Name into grouped
select new MyObjectWithCount
{
Name = grouped.Key,
MatchingObjects = grouped.ToList()
}).AsQueryable();
I am trying to apply a bit of groupby/crosstabbing logic to an IEnumerable list of a user defined object and was wondering if anyone could help me out. I'm stuck with an existing (rather annoying) object model to work with but anyway here goes...
consider the following class which I will condense to only relevant properties so you get the jist...
public class Holding
{
private void Institution;
private string Designation;
private Owner Owner;
private Event Event;
private Shares Shares;
}
I want to convert this into a list that satifys the following...
The object is grouped by Institution.
This parent list of institutions contains a list of a new object with a unique combination of Designation and Owner.
Now for each of this combination of Designation and Owner we get another child list of unique Events.
So it basically 3 lists deep.
I'm not sure if this is possible with an IEnumerable List or not, I have toyed around quite a bit with the GroupBy extension method to no avail thus far. I'd like most to do it this way, but I'm using linq-to-sql to get the initial list of holdings which is as follows and might be the better place to do the business...
public static List<Holding> GetHoldingsByEvents(
int eventId1,
int eventId2)
{
DataClassesDataContext db = new DataClassesDataContext();
var q = from h in db.Holdings
where
h.EventId == eventId1 ||
h.EventId == eventId2
select h;
return q.Distinct().ToList();
}
Any help/guidance would be much appreciated...
Thanks in advance.
I'm using ToLookup method, which is kind of a grouping, it takes two parameters, first one a function used for defining the group keys and the next one is a function used as a selector (what to take from the match).
items.ToLookup(c=>c.Institution.InstitutionId, c => new {c.Designation, c.Owner, c.Event})
.Select(c => new {
// find the institution using the original Holding list
Institution = items.First(i=>i.Institution.InstitutionId == c.Key).Institution,
// create a new property which will hold the groupings by Designation and Onwner
DesignationOwner =
// group (Designation, Owner, Event) of each Institution by Designation and Owner; Select Event as the grouping result
c.ToLookup(_do => new {_do.Designation, _do.Owner.OwnerId}, _do => _do.Event)
.Select(e => new {
// create a new Property Designation, from e.Key
Designation = e.Key.Designation,
// find the Owner from the upper group ( you can use items as well, just be carreful this is about object and will get the first match in list)
Owner = c.First(o => o.Owner.OwnerId == e.Key.OwnerId).Owner,
// select the grouped events // want Distinct? call Distinct
Events = e.Select(ev=>ev)//.Distinct()
})
})
I assumed your classes look like these
public class Holding
{
public Institution Institution {get; set;}
public string Designation {get; set;}
public Owner Owner {get; set;}
public Event Event {get; set;}
}
public class Owner
{
public int OwnerId {get; set;}
}
public class Event
{
public int EventId {get; set;}
}
public class Institution
{
public int InstitutionId {get; set;}
}
Let us say we have a simple business object:
class SimpleBO
{
public string Field1{get;set;}
public string Field2{get;set;}
}
Also we have a complex Aggregate like that:
class ComplexBO
{
public SimpleBO SimpleBOField {get;set}
public List<SomeClass> ObjectList {get;set;}
public SomeClass Object {get;set;}
}
SomeClass itself has a reference of SimpleBO:
class SomeClass
{
public SimpleBO SimpleBOField {get;set}
}
Now in some part of my program I want to get a list of all distinct simple objects met inside a certain aggreggate. We are using automapper heavily but I did not manage to map it so far. May be a LINQ query is a better option? How would you solve this?
Assuming what you have is:
ComplexBO aggregate = ...
then you should just need:
var objects = aggregate.ObjectList.Select(x => x.SimpleBOField).Concat(
new[] {aggregate.SimpleBOField, aggregate.Object.SimpleBOField }
).Distinct().ToList();
This will give you the distinct object references; if you need distinct value pairs, then either override Equals()/GetHashCode(), or cheat:
var objects = aggregate.ObjectList.Select(x => x.SimpleBOField).Concat(
new[] {aggregate.SimpleBOField, aggregate.Object.SimpleBOField }
).Select(
x => new {x.SimpleBOField.Field1, x.SimpleBOField.Field2}
).Distinct().Select(
x => new SimpleBO {Field1 = x.Field1, Field2 = x.Field2}
).ToList();