In my data structures I have the following classes:
public partial class Item
{
// stuff
public int QuoteId { get; set; }
public virtual ItemType ItemType { get; set; }
}
public partial class ItemType
{
//stuff
public virtual ICollection<Item> Items { get; set; }
}
What I want to do is get a list of all the ItemTypes, each of which has its Items collection populated according to a QuoteId.
So, for example if there are three item types, only two of which have items with a quote Id of 50:
ItemType1
Item.QuoteId == 50
ItemType2
ItemType3
Item.QuoteId == 50
I've managed to get something close with this query:
r.ItemTypes.Select(x => x.Items.Where(i => i.QuoteId == CurrentQuote.QuoteId));
But what this gives you (as you might expect, since I'm Selecting on Item) is an IEnumerable<IEnumerable<Item>>. This has the structure that I'm after but doesn't have the ItemType data.
I realise this is a dumb question, but I'm frustrated by my inability to get the answer.
r.ItemTypes.Where(x => x.Items.Any(i => i.QuoteId == CurrentQuote.QuoteId));
If you need to get all ItemTypes and only specific Items for every, you can do this:
r.ItemTypes.Select(x => new
{
x,
FilteredItems = x.Items.Where(i => i.QuoteId == CurrentQuote.QuoteId)
});
After that you need to assign x.Items to FilteredItems for every ItemType
You have to select the Item.ItemType property if you want the all ItemTypes of a given QuoteId. You also have to use SelectMany to flatten the "nested" collections:
IEnumerable<ItemType> types = r.ItemTypes
.SelectMany(x => x.Items.Where(i => i.QuoteId == CurrentQuote.QuoteId)
.Select(i => i.ItemType));
If you are not interested in the nested ItemType(don't know the logic) you can use Backs' approach:
IEnumerable<ItemType> types = r.ItemTypes
.Where(x => x.Items.Any(i => i.QuoteId == CurrentQuote.QuoteId));
var result = from itemTypes in r.ItemTypes
where itemTypes.QuoteId equals CurrentQuote.QuoteId
select new {itemType = itemTypes}
Related
I'm having a hard time with this approach since I am new to Entity Framework. I actually don't know if there is something special related to EF or if the limitation is on me.
I would like to group some records from my database and after that, I'd like to iterate over the groups, then iterate over the elements on each group comparing it with all the other elements in the same group.
I have created two simple classes to illustrate the scenario:
public class MyContext : DbContext
{
public DbSet<MyClass> MyClass { get; set; }
}
And:
public class MyClass
{
public int Id { get; set; }
public int Value { get; set; }
}
What I have so far with a context injected is:
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
// compare each item with all the other
// items in the current group
});
But I don't know how to iterate over the items and then compare with the others in the same group.
With the following code, the quesiton becomes what type is grp?
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
// compare each item with all the other
// items in the current group
});
Well the grp variable is of type IGrouping<TKey, TElement>. That type derives from IEnumerable<TElement> so each grp is a list of TElement so you can foreach or do whatever you want to all the items in the grp.
DotNetFiddle Example.
Your variable grp is an IGrouping<int, MyClass>. You can treat it as an IEnumerable<MyClass>. For instance, you can get the item with the biggest Id like this:
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
MyClass itemWithMaxId = grp.FirstOrDefault();
foreach (MyClass item in grp)
{
if (item.Id > itemWithMaxId.Id)
{
itemWithMaxId = item;
}
}
});
Note, however, that the ForEach method does not return anything, it only performs the specified action on each element of the list. If you want to get something, for instance the item with the biggest Id of each group, I suggest you to use the Select method provided by Linq, like in this example:
var itemsWithMaxIdByGroup = this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.Select(grp =>
{
MyClass itemWithMaxId = grp.First();
foreach (MyClass item in grp.Skip(1))
{
if (item.Id > itemWithMaxId.Id)
{
itemWithMaxId = item;
}
}
return itemWithMaxId;
});
I have the following basic classes (cut down for this question):
public class Parent
{
public string Name { get; set; }
public IList<Child> Children { get; set; }
}
public class Child
{
public string Name { get; set; }
}
If I have a Parent collection, what I'd like to do is get an IList that is sorted by Parent.Name and also the Children for each parent need to be sorted by their Name.
I've tried this (which only sorts the Parents, not the Children):
IList<Parent> parents = ... //Populated
parents.OrderBy(p => p.Name).ThenBy(p => p.Children.OrderBy(c => c.Name)).ToList()
I've searched but can't find anything (probably me being dumb).
Any suggestions for a Linq newbie?
Thanks in advance
Andy
First of all, calling OrderBy on the list, the way you do, won't sort it in-place. It will return a new sorted IEnumerable; you can use .ToList() on that to turn it into a list, but it will still be a copy. Now on to the sorting itself. You really need to not just order the items in the collection, but make a copy of each item which would have its Children sorted as well. So:
IList<Parent> parents = ... //Populated
parents = (from p in parents
orderby p.Name
select new Parent
{
Name = p.Name,
Children = p.Children.OrderBy(c => c.Name).ToList()
}
).ToList();
Same solution, using LINQ method syntax:
IList<MyType> myTypeList = ... //Populated
var sortedList = myTypeList.Select(t =>
{
t.Children = t.Children.OrderBy(c => c.Name).ToList();
return t;
}).ToList();
I have two lists, one which is a list of Equipment and one which is a list of WorkflowItems with an Equipment property. The Equipment property within the List<WorkflowItem> list only has one of it's values hydrated, ProcessId. The List<Equipment> has two properties hydrated, ProcessId and Name. I want to hydrate the List<WorkflowItem>.Equipment.Name with the value from the single Equipment record in the List<Equipment>
This LINQ query below will select a generic item out doing basically what I'm looking for, but I would rather just fill in the original list.
var list = from item in workflowItems
join equipment in barcodeEquipmentList on
item.Equipment.ProcessId equals equipment.ProcessId
select new
{
ProcessId = item.Equipment.ProcessId,
EquipmentName = equipment.Name
};
Edit
The list is going to be relatively small, even doing something like this would be fine (aside from the fact that this does not work)
workflowItems.ForEach(x => x.Equipment = from e in barcodeEquipmentList
where e.Process.Id == x.Equipment.Process.Id
select e
);
...final edit
but this does work:
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());
This piece of code should match your needs:
public class Equipment {
public int ProcessId { get; set; }
public string Name { get; set; }
}
public class WorkflowItem {
public Equipment { get; set; }
public void LoadEquipmentFrom(IEnumerable<Equipment> cache){
var equipment = cache.FirstOrDefault(e => e.ProcessId == Equipment.ProcessId);
if(equipment != null)
Equipment.Name = equipment.Name;
}
}
You could also assign the instance from cache to the existing one, it wouldn't matter since both must have the same Identifier Equipment = equipment;. That would be easier if you have more properties to set. To optimize further use an IDictionary<int, Equipment> instead of that IEnumerable<Equipment>, because you'll be reading that collection very often.
I'm guessing you are implementing a kind of ORM, in this case I can give you a good advice: "There is already something out there that'll fit your needs.".
Since the dataset was not very big (less than 20 records), I was able to this as below without a hit to performance
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());
Below is my class.
public class MyGroceryListItems
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
}
Now I am fetching from database the values for this class and putting those classes in IList<MyGroceryListItems>. However, there are some duplicate items with same id but different quantity. How can I fetch only distinct entries by id?
I tried list.distinct but that didn't work because not all the entries are same in the record having same product id.
IEnumerable<MyGroceryListItems> items = ...;
var uniqueItemsByProdId =
items.GroupBy(x => x.ProductId).Select(g => g.First());
This will pick off a single (and somewhat arbitrary) item in the case that more than a item shares a ProductId with another.
Alternatively (and slightly faster), you could use a DistinctBy extension:
public static IEnumerable<T>
DistinctBy<T,TKey>(this IEnumerable<T> src, Func<T,TKey> selector)
{
HashSet<TKey> hs = new HashSet<TKey>();
foreach(var item in src)
{
//Add returns false if item is already in set
if(hs.Add(selector(item)))
{
yield return item;
}
}
}
like this:
items.DistinctBy(x => x.ProductId)
More useful, perhaps, is a query that gives the aggregate quantities for each item by ProductId:
items
.GroupBy(x => x.ProductId)
.Select(g => new MyGroceryListItems{
g.Key.ProductId,
g.Key.ProductName,
Quantity = g.Sum(gg => gg.Quantity)
})
You can implement an equality comparer. There is a solid example on msdn: http://msdn.microsoft.com/ru-ru/library/bb338049.aspx
This will give you more control over which items you consider equal. But involves more coding. If you want to select single and somewhat random item from all the items which share an id, then you'll be better off with spender's solution
I have a class structure something like this:
class MyClass
{
public IEnumerable<AttributeGroup> AttributeGroups { get; set; }
}
class AttributeGroup
{
public IEnumerable<Attribute> Attributes { get; set; }
}
class Attribute
{
public string SomeProp { get; set; }
}
I need to get all 'Attributes' which has a specific 'SomeProp' value no matter which Attribute Group they belong to.
For example, SomeProperty== 'A' can be found in both MyClassObj.AttributeGroup[0] and MyClassObj.AttributeGroup[5] and I need to write a Linq (or something like that) to fetch two objects from these two different attributegroups.
Any suggestion?
First select all attributes from all attribute groups, then only select the ones with your property.
IEnumerable<Attribute> attributes =
myClassInstance
.AttributeGroups
.SelectMany(x => x.Attributes)
.Where(x => x.SomeProperty == 'A');
Other Linq-style syntax:
IEnumerable<Attribute> attributes =
from attributeGroup in myClassInstance.AttributeGroups
from attribute in attributeGroup.Attributes
where attribute.SomeProperty == 'A'
select attribute;
Have a look at SelectMany (http://msdn.microsoft.com/en-us/library/bb534336.aspx).
For example:
myClassObjs.SelectMany(o => o.AttributeGroups.SelectMany(g => g.Attributes)).Where(a => a.SomeProp == "A")
This line selects all Attribute objects of all AttributeGroups of all MyClass objects where SomeProp equals "A". a in the lambda expression for Where is of type Attribute.
Your example isn't clear; I can't tell what you mean by, "two object from these two different attributegroups". I'll guess that you want the groups that have attributes with the property in question:
from g in MyClassObj.AttributeGroups
where g.Any(attr => attr.SomeProperty == "A")
select g