Group by for list in list - c#

I have list of Issues : List<Issue> where Issue is class :
public class Issue : BaseEntity
{
private string m_KeyString;
[JsonProperty("expand")]
public string Expand { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
#region Special key solution
[JsonProperty("key")]
public string ProxyKey
{
get
{
return Key.ToString();
}
set
{
m_KeyString = value;
}
}
[JsonIgnore]
public IssueKey Key
{
get
{
return IssueKey.Parse(m_KeyString);
}
}
#endregion Special key solution
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
Class Fields looks like this :
public class Fields
{
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("assignee")]
public Assignee Assignee { get; set; }
[JsonProperty("worklog")]
public List<WorkLog> WorkLogs { get; set; }
}
And class WorkLog:
public class WorkLog : BaseEntity
{
[JsonProperty("updateAuthor")]
public string Author { get; set; }
[JsonProperty("timeSpent")]
public string TimeSpent { get; set; }
[JsonProperty("timeSpentSeconds")]
public int TimeSpentSeconds { get; set; }
}
I want to get on output Author - SUM (TimeSpentSeconds)
So i need to group by Author and then get SUM.
For each item in Issues list I make this :
var sumTime = issue.Fields.WorkLogs.GroupBy(x => x.Author).Select(x => new
{
User = x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
});
which group by user and count sum.
But how can i manage the same not just for one item in list but for all list?

OK, I think you're saying that you can run this query for a single Issue, but you want to be able to run it over all the items in a List<Issue>.
I'm assuming that you don't want to break it out separately by Issue, so what I'd do is this:
List<Issue> issues = // get Issues from wherever
var sumTime = issues.SelectMany(issue => issue.Fields.WorkLogs)
.GroupBy(x => x.Author)
.Select( x => new
{
x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
}
);
Disclaimer:
this was done entirely from memory, with no testing.

Related

ASP NET MVC 6 - How to get Product count for each enum value

I have developed a Site using asp net mvc 6, I have used enom as categories, now I need to get product count under each enom value,
Here is my enum look like,
namespace ecom.Data
{
public enum BookCategory
{
Action,
Comedy,
Drama,
Others
}
}
What I want is get products count under this enoms,
See here, Front view without product count
This is my Controller Looks like,
using ecom.Data.Services;
namespace ecom.Controllers;
public class HomeController : Controller
{
private readonly IBooksService _service;
public async Task <IActionResult> BookView(string slug)
{
var data = await _service.GetBookBySlugAsync(slug);
if(data == null) return View("NotFound");
return View("BookView", data);
}
This is model which i use as product model,
namespace ecom.Models
{
public class Book:IEntityBase
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string Image { get; set; }
public string PublishDate { get; set; }
//enom
public BookCategory BookCategory { get; set; }
//Relationships
public List<Writter_Book> Writter_Books { get; set; }
//Publisher
public int PublisherId { get; set; }
[ForeignKey("PublisherId")]
public Publisher Publisher { get; set; }
}
}
My cshtml view right now,
#foreach (var cat in Html.GetEnumSelectList<BookCategory>())
{
<li>#cat.Text <span>()</span></li>
}
I want to get product count to above inside foreach's span tag,
Anyone can help me with that??
You can use GroupBy and Count to achieve your goal.
Something like
myBooksDataSource
.GroupBy(book => book.BookCategory)
.Select(group => new { Category = group.Key, Count = group.Count() });
Which will output a collection of (BookCategory, Count).
{ BookCategory.Action, 20 }
{ BookCategory.Comedy, 204 }
You can create a class to hold that data if you need to pass it around.
public class BookCategoryCountData
{
public BookCategory Category { get; set; }
public int Count { get; set; }
}
and select using that object.
public IEnumerable<BookCategoryCountData> GetBookCountByCategory()
{
return _context.Books
.GroupBy(book => book.BookCategory)
.Select(group => new BookCategoryCountData() {
Category = group.Key,
Count = group.Count()
});
}

Sort nested properties on 3 Lists

How would one sort nested lists in place:
List of JobCollection
List of Jobs (sort by string Sponsor)
List of Items (sort by int Order)
Data model class structure:
public class JobCollection
{
public string Collection { get; set; }
public virtual TrulyObservableCollection<Job> Jobs { get; private set; } = new TrulyObservableCollection<Job>();
}
public class Job
{
public virtual JobCollection JobCollection { get; set; }
public string JobGUID { get; set; } = Guid.NewGuid().ToString();
public string Sponsor { get; set; }
public virtual TrulyObservableCollection<Item> Items { get; private set; } = new TrulyObservableCollection<Item>();
}
public class Item
{
public virtual Job Job { get; set; }
[Key]
public string ItemGUID { get; set; } = Guid.NewGuid().ToString();
public int Order { get; set; }
}
I've tried various ways but not getting anywhere with it such as:
EntireCollection.SelectMany(o => o.Jobs).ToList().ForEach(d => d.Sponsor = d.Items.OrderBy(e => e.Order).ToList());
elem => elem.Jobs.OrderBy(
job => job.Items.OrderBy(
item => item.Order
)
Argh!
The easiest way to do it, but not suggested.
jobCollection.ForEach( (jobCol) =>
{
jobCol.Jobs.ForEach( (job) =>
{
job.Items.OrderBy( item => item.Order );
});
jobCol.Jobs.OrderBy( (job) => job.Sponsor );
});

order by nested class property

I have following class structure
public class PriceClass
{
public int id { get; set; }
public string price { get; set; }
public int product_id { get; set; }
}
public class NameClass
{
public int id { get; set; }
public string name { get; set; }
}
public class ProductDetails
{
public int id { get; set; }
public string product_type { get; set; }
public List<NameClass> nameCl{ get; set; }
public List<PriceClass> priceCl{ get; set; }
}
public class ProductLists
{
public List<ProductDetails> ProductDet{ get; set; }
}
Now I want to sort the ProductLists with price i.e. in priceClass class
I tried with some solution but that does not seems working
prdList = (ProductLists )prdList.ProductDet.OrderBy(r => r.priceCl.OrderBy(x => x.price).ToList());
But it seems i am no where around its solution
Please suggest
Thanks
It is just simpler:
sorted_ProductDet_List = prdList.ProductDet
.OrderBy(prDet => prDet.sortPrice)
.ToList());
Then you'll have to add a Property sortPrice to priceDetail which returns the relevant price used as sort criterium.
The lambda inside OrderBy works on a single element of the original List (List) and specifies the variable/property/etc (probably from any nested class or method) the sorting is based on.
You can use a custom IComparer to compare the product details.
public class ProductDetailsComparer : IComparer<ProductDetails>
{
private bool _compareMinPrice;
public ProductDetailsComparer(bool compareMinPrice)
{
_compareMinPrice = compareMinPrice;
}
public int Compare(ProductDetails x, ProductDetails y)
{
var left = _compareMinPrice ? x.priceCl.Min(p => p.price) : x.priceCl.Max(p => p.price);
var right = _compareMinPrice ? y.priceCl.Min(p => p.price) : y.priceCl.Max(p => p.price);
return left.CompareTo(right);
}
}
You can use the custom comparer in OrderBy as shown below.
// Sort by minimum price
var ascending = productLists.ProductDet.OrderBy(x => x, new ProductDetailsComparer(true)).ToList();
// Sort by maximum price
var descending = productLists.ProductDet.OrderBy(x => x, new ProductDetailsComparer(false)).ToList();
The above is just a sample. You can write a custom comparer suiting your requirements.

Filtering LINQ child collection EF

Im having some problems trying to do this filtering and im sure it can be done better than what im doing. I will show my classes and how im solving it but i was wondering if I could use Linq to filter this. My Classes:
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public virtual List<FeatureType> Features { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<FeatureType> FeatureTypes { get; set; }
public override string ToString()
{
return Name;
}
}
public class FeatureType
{
public int Id { get; set; }
public string Name { get; set; }
public Section Section { get; set; }
public virtual List<ItemType> ItemTypes { set; get; }
}
I'm trying to get all Sections, and filter Features by an ItemTypeID, so only the FeatureTypes of that ItemTypes are listed. What Im doing now its just getting all sections, and just do a for and just add the ones that work for me in other:
public ItemTypeFeatureViewModel(int myItemTypeId, IUnitOfWork myUnitOfWork)
{
ItemTypeId = myItemTypeId;
unitOfWork = myUnitOfWork;
Sections = unitOfWork.SectionRepository.Get(includeProperties: "Features")
.ToList();
foreach (var item in Sections)
{
var x = new List<FeatureType>();
foreach (var feature in item.Features)
{
foreach (var itemType in feature.ItemTypes)
{
if (itemType.Id == ItemTypeId)
{
x.Add(feature);
break;
}
}
}
item.Features = x;
}
}
Can i improve this and avoid all this foreach?
You can't filter out included collection on server side, but you can replace two inner loops with:
item.Features = item.Features
.Where(f => f.ItemTypes.Any(i => i.Id == ItemTypeId))
.ToList();
That will select only those features which have at least one item type with id you provided.
Try the following:
Sections
.ForEach(x => x.Features = x.Features.Where(y => y.Any(z => z.Id == ItemTypeId))
.ToList());

How can I get the data from fields of an .Include in a LINQ Query?

I am using Entity Framework 5 and I have these classes. What I want to do is to be able to get the data to populate the view listed below:
public partial class Subject
{
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Topic> Topics { get; set; }
}
public partial class Topic
{
public int TopicId { get; set; }
public string Name { get; set; }
public int SubjectId { get; set; }
public virtual Subject Subject { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic
{
public int SubTopicId { get; set; }
public string Name { get; set; }
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
Now I am trying to write a LINQ query to populate this class:
public class TopicSubTopicSelect
{
public int TopicId { get; set; }
public int SubTopicId { get; set; }
public string TopicName { get; set; }
public string SubTopicName { get; set; }
}
So far I have this:
return _subjectsRepository
.GetAll()
.Where(s => s.SubjectId == subjectId)
.Include(s => s.Topics.SelectMany(t => t.SubTopics))
.AsEnumerable()
.Select(item => new TopicSubTopicSelect(item.TopicId <<<
item.SubTopicId <<
item.Topic.Name <<
item.Name <<))
.ToList();
Can someone tell me how I can get data from the fields I marked with <<. I tried to do .item.Topic.TopicId etc but that does not seem to work.
You shouldn't start from Subject. You just start from SubTopic Repository, and you won't even need to use .Include. Do it like this:
_subTopicRepository
.GetAll()
.Where(s => s.Topic.SubjectId == subjectId)
.Select(s => new TopicSubTopicSelect()
{
TopicId = s.TopidId,
SubTopicId = s.SubTopicId,
TopicName = s.Topic.Name,
SubTopicName = s.Name
})
.ToList();
As I mentioned in my comment on ataravati's answer, you shouldn't actually have a SubTopicRepository so you are correct in starting at SubjectsRepository however you are querying by the Subject ID so you shouldn't be going via GetAll(), you should have a Get(int id) method. The include should be handled as an implementation detail inside Get as the children (SubTopics) are part of the Subject. That makes the method call look like this instead:
return _subjectsRepository
.Get(subjectId)
.SelectMany(subject => subject.SubTopics))
.Select(subTopic => new TopicSubTopicSelect
{
TopicId = subTopic.TopicId,
SubTopicId = subTopic.SubTopicId,
TopicName = subTopic.Topic.Name,
SubTopicName = subTopic.Name
}).ToList();

Categories

Resources