I use GroupBy to grouping objects like:
var categories=
tovRDb.MyObjects.AsEnumerable().Where(t => myHashSet.Contains(t.Id))
.GroupBy(t => t.Specialization, t => t,
(key, g) => new {Name = key.Name, CategoryItems = g.ToList()})
.OrderBy(t => string.IsNullOrEmpty(t.Name))
.ThenBy(t => t.Name)
.ToList();
It's works fine.
But now i want to group objects where Specialization is ICollection<>.
For example:
MyObject1: "Berlay's Meat"
Specializations{Chicken, Pork, Beef}
MyObject2: "Wonday's Chickery"
Specializations{Chicken}
MyObject3: "Rooki's Meat"
Specializations{Chicken, Pork}
And after group by:
Pork{MyObject1: "Berlay's Meat",MyObject3: "Rooki's Meat"}
Beef{MyObject1: "Berlay's Meat"}
Chicken{MyObject1: "Berlay's Meat",MyObject2: "Wonday's Chickery", MyObject3: "Rooki's Meat"}
Any advises?
Withcategories as:
var categories = tovRDb.MyObjects.AsEnumerable().Where(t => myHashSet.Contains(t.Id));
var catsGrouped = categories.SelectMany(
x => x.Specializations, // Specializations is an IEnumerable<Specialization>
(x, y) => new
{
Category = x,
Specialization = y,
}).GroupBy(x => x.Specialization, x => x.Category)
.ToArray();
I used the SelectMany to "multiply" each category for its specializations... Then I regrouped the result by Specialization. The result is a IGrouping<Specialization, MyObject>[]
Related
I have a list of guids as string:
This is how i retrive my list of string guids:
List<string> p0 = ctx.PrProjectRating.Select(k => k).GroupBy(g => new { g.PrPIdG }, (key, group) => new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG }).Select(t => t.pidG).ToList();
Now i have another list that contains a field called pidG but this list needs to be ordered by the list of guid strings above.
How do i achiveve this.
i tried:
List<PProject> p = p.OrderBy(c => p0.Contains(c.PIdG)).ToList();
but still the list is not ordered by the string guids in the first list "p0"
You have to do join here
List<string> p0 = ctx.PrProjectRating
.Select(k => k)
.GroupBy(g => new { g.PrPIdG }, (key, group) =>
new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG })
.Select(t => t.pidG).ToList();
var result = p0.Join(p, x => x, c => c.PIdG, (x, c) => c)
.ToList()
I have a scenario where in case there is a specific boolean value satisfied (office_debit_total line) I can get amount directly from a column otherwise I need to calculate it by grouping some specific values, here's the code:
var result = products.Select(p => new ResponseDto()
{
customer_id = p.CustomerId,
office_debit_date = p.OfficeDebitDate.Value.ToString(),
office_debit_id = p.OfficeDebitId.ToString(),
office_debit_total = p.OfficeEnum == SomeEnum.ValueType ? p.OfficeAmount.ToString() : totalAmounts[p.OfficeDebitId].ToString(),
payment_method = p.PaymentMethod.Value.ToString(),
}).ToList();
As it's possible to be seen office_debit_total is calculated depending on enum value, and here's dictionary that I'm using to get grouped data:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key.OfficeDebitId, x => x.Sum(p => p.Amount));
But I have receiving following error message:
An item with the same key has already been added.
I've tried writing .ToLookup instead of .ToDictionary but that didn't helped me..
Thanks guys
Cheers
If your dictionary has only OfficeDebitId as key then you need to group by only by it:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
or use full anonymous object as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Or with value tuple as key:
var totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => (p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod))
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
Why not this:
Dictionary<string, decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => p.OfficeDebitId)
.ToDictionary(x => x.Key, x => x.Sum(p => p.Amount));
You might need it in this way (You can use value tuple):
Dictionary<(string OfficeDebitId, System.DateTime? OfficeDebitDate, Enumerations.PaymentMethod? PaymentMethod), decimal> totalAmounts = products
.Where(p => p.ProductType == ProductType.ValueType)
.GroupBy(p => new { p.OfficeDebitId, p.OfficeDebitDate, p.PaymentMethod })
.ToDictionary(x => (x.Key.OfficeDebitId, x.Key.OfficeDebitDate, x.Key.PaymentMethod ), x => x.Sum(p => p.Amount));
I've been asked to simplify the following Linq query:
var orders = db.Orders
.Join(db.Shipments,
o => o.OrderID,
s => s.OrderID,
(o, s) => new { Order = o, Shipment = s })
.Join(db.LineItems,
s => s.Shipment.ShipmentID,
l => l.ShipmentID,
(s, l) => new { Order = s.Order, Shipment = s.Shipment, LineItem = l })
.Join(db.StatusTypes,
s => s.Shipment.StatusTypeID,
st => st.StatusTypeID,
(s, st) => new { Order = s.Order, Shipment = s.Shipment, LineItem = s.LineItem, Description = st.ExternalDescription })
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
By replacing the Joins with Includes. I've searched around, and I've discovered that Joins and Includes are somewhat equivalent. But I can't figure out how to convert this query to one that uses includes instead of joins. Is it actually less code and simpler to use includes instead of joins here?
I strongly suggest you to use navigation properties instead of manual builded joins. It will be more efficent and controlable. Read this article.
If you would convert your query to navigation property form by using Include, it would seem like that;
var orders = db.Orders
.Include(x => x.Shipments)
.Include(x => x.Shipments.Select(y => y.LineItems))
.Include(x => x.Shipments.Select(y => y.StatusType))
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Created),
OrderNumber = x.Key,
Cost = x.LineItems.Sum(k => k.UnitPrice),
Status = x.Max(y => y.Description)
}
);
But, as I said, you should define the navigation properties for entities first.
var orders = db.Orders
.Include("Shipments")
.Include("Shipments.LineItems")
.Include("Shipments.StatusTypes")
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
i not tested above code ,just try
ObjectQuery.Include Method (String)
This query below doesn't work because String.Join is not translatable.
PostgreSQL has the string_agg(expression, delimiter) feature though.
Is there anyway to use it from Linq?
var vwTourWithCategorieses = Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName },
(key, c) => new VwTourWithCategories
{
TourId = key.TourId,
Name = key.TourName,
Categories = string.Join(",", c.Select(i => i.CategoryName))
})
.ToList();
Yes, unfortunately String.Join is not supported by EF, but I think you could project the result that you expect using Linq to objects after you materialize your query:
var query= Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName }).ToList()
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
If you want to see all the CLR methods that are supported, you can check this link.
Update
Your query could be simpler if you use navigation properties. I think that is a many to many relationship, so you could do something like this:
var query= Context.Tours.Select(t=> new
{
t.TourId,
t.Name,
CategoryNames = t.TourCategories.Select(tc=>tc.Category.Name)
}
).ToList();
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
I am trying to convert a DataTable of the form
Key Value
1 A
1 B
1 C
2 X
2 Y
To a Dictionary
1 [A,B,C]
2 [X,Y]
The lambda expression I am using is
GetTable("..sql..").AsEnumerable().
.Select(r => new {Key = r.Field<int>("Key"), Val = r.Field<string>("Value")})
.GroupBy(g => g.Key)
.ToDictionary(a => a.Key, a => String.Join(",", a.Value))
But it fails with "Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type"
How can I accomplish this?
This does it:
GetTable("..sql..").AsEnumerable().
.Select(r => new {Key = r.Field<int>("Key"), Val = r.Field<string>("Value")})
.GroupBy(g => g.Key)
.ToDictionary(a => a.Key, a => String.Join(",", a.Select(x => x.Value).ToList()))
Here's another way you can do it...
GetTable("..sql..").AsEnumerable()
.GroupBy(x => x.Field<int>("Key"))
.ToDictionary(grp => grp.Key, x => x.Select(y => y.Field<string>("Value")).ToList());
var foo = GetTable("").AsEnumerable()
.ToLookup(x => x.Key, x => x.Value);
foreach(var x in foo)
{
foreach(var value in x)
{
Console.WriteLine(string.Format("{0} {1}", x.Key, value));
}
}