Sum list of objects within list with Linq - linq

I have a list of Teams which contains another list of Players and every Player has played games number, I want to sum every played game that a player has grouped by Team.
How can I do a linq function to get that result?
I have tried something like that but I got stuck
var ListResultQuery = students.SelectMany(books=>books.Scores.Where(book=>book.Price>5).GroupBy(b=>b.Name));
My project Currently is:
public class Team{
public string Name{get;set}
public ICollection<Player> PlayerList{get;set}
}
public class Player{
public string Name{get;set}
public int playedGames{get;set}
}

Related

Query Realm with multiple backlinks

The Realm documentation gives an example of backlinks using a person object and a dog object. If I extend this to include cats also, so a person can have several dogs or cats to walk, and each dog or cat can be walked by several different people.
public class Dog : RealmObject
{
public string Name { get; set; }
[Backlink(nameof(Person.Dogs))]
public IQueryable<Person> Walkers { get; }
}
public class Cat : RealmObject
{
public string Name { get; set; }
[Backlink(nameof(Person.Cats))]
public IQueryable<Person> Walkers { get; }
}
public class Person : RealmObject
{
//... other properties (name, age, address, etc.)
public IList<Dog> Dogs { get; }
public IList<Cat> Cats { get; }
}
Using the backlinks lets me get a list of people who walk the dog Fido...
var fidoWalkers = realm.All<Dog>().Where( d => d.Name == "Fido").FirstOrDefault().Walkers;
I can now further expand this query to find walkers of Fido who live in High Street or who are under 30 years old or whatever... great so far.
Now I want to get a list of people who walk the dog Fido and the cat Moggie. Using the backlinks in two separate statements I could get two result sets, one for Fido walkers and one for Moggie walkers, but I don't know how to combine them. Neither can I work out a query that would let me do this 'the long way round' without using the backlinks, because whenever I try to use
...Where( P => p.Dogs.Contains(Fido))...
I get 'System.NotSupportedException: The method 'Contains' is not supported'
Is there any way to get a list of people filtered by both the Dogs and the Cats lists?
While there are many things the .Net version of Realm does well, there are limitations in the Linq support and thus you are forced to materialize the lazy loaded IRealmCollection to a hard List (or array) via LINQ to Objects in order to perform projections, joins, IEqualityComparer comparisons, etc... this includes Backlinks and RealmObject-based IList properties.
A must read Realm-dotnet document: LINQ support in Realm Xamarin
You could add couple of properties that perform a Linq projection to a list of strings (pet names, of course assuming they are unique keys):
public class Person : RealmObject
{
//... other properties (name, age, address, etc.)
public IList<Dog> Dogs { get; }
public IList<Cat> Cats { get; }
public List<string> DogList => Dogs.ToList().Select(_ => _.Name).ToList();
public List<string> CatList => Cats.ToList().Select(_ => _.Name).ToList();
}
And then take your person query to a list and use Contains on the materialized string lists vs. the RealmCollection properties.
var walkers = realm.All<Person>().ToList().Where(_ => _.CatList.Contains("Garfield") && _.DogList.Contains("Fido"));
foreach (var walker in walkers)
{
Log.Debug(TAG, $"{walker}");
}
You could also create two queries, one filtered on the dog's name backlink and cats for the other, materialized all the Dog and Cat collections on each Person in both queries, perform a Distinct via a custom IEqualityComparer.
Once you have copied the objects out of Realm into independent lists/arrays, just about any Linq solution will work...
The downside of this is all the filtering is being performed in memory thus you take a memory and CPU hit as you are not able to use Realm's native query features...
Personally, I would re-model the RealmObject's to provide people that walk animals and thus animals are generic and they in turn provide links to particular Dog and Cat RealmObjects that provide specialization. But that is totally without knowing your end goal...

Trying to re-arrange pages of a PDF in a sorted order

I have a program (Shipstation) that provides me with packing lists as a PDF document (one page per order). Shipstation sorts by order number, and doesn't allow me to sort the pages any other way.
I wondering if there is a way to rearrange pages so they are sorted by first name of the recipient?
I'm including a link to an example packing list here.
Thanks!
John
Step 1: Sort your abstract pages such that you have an array/list such that the 0th element is an integer representing the actual page that should go there
Step 2: Using a PDF toolkit of some kind, reorder the document
Now, let's talk about step 1 - let's assume that you have the data that you want in something like this:
public class Invoice {
public string FirstName { get; set; }
public string LastName { get; set; }
public int OrderNumber { get; set; }
}
Now you would have List and you could easily sort it by OrderNumber. If you do this, the order of your list should correspond to pages in the output PDF.
If you resort by FirstName into a different list, you can now generate the page transitions as a list or array of int.
Step 2: this is going to depend on what toolkit you use. I'm sure you could use iText or docotoic.pdf, but I'm going to use DotPdf or JoltPdf as examples libraries (mostly because I wrote them and this is a slam-dunk task):
public static void ResortPdf(Stream inPdf, Stream outPdf, List<int> pageTransitions)
{
PdfDocument doc = new PdfDocument(inPdf, null, null); // no passwords - but maybe you need that?
PdfPage[] originalPages = doc.Pages.ToArray();
for (int i=0; i < pageTransitions.Count; i++) {
doc.Pages[i] = originalPages[pageTransitions[i]];
}
doc.Save(outPdf);
}

OrderBy Parent object by Nested Collection

Simple
public class TimeLine
{
public String Name {get;set}
public List<Milestone> MilesStones {get;set}
}
public class Milestone
{
public String Name {get;set}
public DateTime Time {get;set}
}
I tried:
from t in DataAccess.TimelineCollection.OrderBy(c=>c.MilesStones.OrderBy(z=>z.MilestoneDate)) select t; but got an error that "At least one object must implement IComparable."
I need to order TimeLine by Milestone.Time. First project in a list will be the one that has eraliest Time property in Milestone collection.
Need help with link.
It sounds like you might want
var query = DataAccess.TimelineCollection
.OrderBy(t => t.MileStones.Min(m => m.Time));
In other words, for each TimeLine, find the earliest milestone, and use that for ordering.
Of course if the milestones are in order, you could use:
var query = DataAccess.TimelineCollection
.OrderBy(t => t.MileStones.First().Time);
Both of these will fail if any TimeLine has no milestones.

MVC View Model Organization

I'm new to MVC and I'm trying to understand how to organize my ModelViews. The first problem is Drop Down data. I have an Equipment Model and an EquipmentViewModel that looks like this:
public class EquipmentViewModel
{
public Equipment Equipment { get; private set; }
public SelectList EquipmentCategories { get; private set; }
private MyEntities db = new MyEntities();
public EquipmentViewModel(Equipment equipment)
{
Equipment = equipment;
EquipmentCategories = new SelectList(db.EquipmentCategories.Where(c => c.IsActive),
"EquipmentCategoryID", "Description");
}
Please note the SelectList for my category dropdown. This is all well and good. However I have another Model called Inventory. Inventory has an EquipmentID property (corresponding to a piece of equipment you see). For the creation of an inventory item it would be useful to have a drop down for the equipment category. I already have this select list in my EquipmentViewModel and it feels wrong some how to duplicate that code for the InventoryViewModel.
I have considered something like this:
public class InventoryViewModel
{
MyEntities db = new MyEntities();
public Inventory Inventory { get; set; }
public EquipmentViewModel EquipmentViewModel { get; set; }
}
This seems okay to me except I am going to have an Index page for the InventoryViewModel. Basically I would be returning a List of InventoryViewModels which each has an EquipmentViewModel where in each of those has the exact same list of categories. This, too, feels wrong and I think I am misunderstanding some crucial piece of the MVC puzzle.
It also leads me to my second question: How would I return such a monstrosity from the controller? I imagine it looking something like this:
var list = db.Inventories
.Select(i => new InventoryViewModel
{
Inventory = i,
EquipmentViewModel = new EquipmentViewModel(i.EquipmentID)
});
Which means that I'd basically be making separate trips to the database (inside the EquipmentViewModel constructor) for each EquipmentID rather than being able to join on the id. For example if I just needed the description I could do this:
var list = from i in db.Inventories
join e in db.Equipments
on i.EquipmentID equals e.EquipmentID
select new InventoryViewModel
{
Inventory = i,
EquipmentName = e.Description
};
Which I believe would have much better performance. I greatly appreciate any wisdom that anyone could offer. Thanks!
I have and idea for you,
there is no point of ducplicating your views if they are doing same thing. however if they are not doing exactly the same is better to exted or create new view than have one big one that is shared for all parts.
You can have model such as this
public class InventoryViewModel{
// contains all categories for every inventory
public SelectListItem EquipmentSelect {get;set;}
//this will contain all the items you want to assign
public List<Inventories> ListOfInventories{get;set;}
}
after in view you can create partial view for every item with the select list item in to it

How to convert IOrderedEnumerable<T> to PagedList<T> which uses OrderBy and ThenBy?

suppose you have a class Product, with members that are declared as nested type ProductFeature:
public class Product {
public ProductFeature ProductID {get;set;}
public ProductFeature ProductName {get;set;}
}
public class ProductFeature {
public int FeatureID {get;set;}
public string FeatureName {get;set;}
}
and somewhere you have a method to load all products as a PagedList<Product>:
var list = db.GetProductList();//returns PagedList<Product>:
now you want to filter and apply some OrderBy and ThenBy:
var sorted = model.Products.OrderBy(x => x.ProductName).ThenBy(x=>x.ProductID);
the result of sorted can be treated as IEnumerable<T> and also IOrderedEnumerable<T>.
the problem is when we try to convert sorted back to PagedList<Product>, or List<Product>.
base {System.SystemException}: {"At least one object must implement IComparable."}
Message: "At least one object must implement IComparable."
Any way to convert sorted as a List again?
Your first problem is that Product.ProductName and Product.ProductID are instances of ProductFeature which does not implement IComparable and therefore your OrderBy and ThenBy fail when you try to enumerate over the results of sorted. This is clearly what the exception message is telling you.
Your second problem is that you didn't tell us what you mean by "convert" when you say "convert sorted back to PagedList<Product>, or List<Product>." However, I suspect whatever you mean will actually be resolved by making ProductName and ProductID instances of something that implement IComparable.

Resources