If I try:
List<Student> students = new List<Student>();
List<Group> Groups = new List<Group>();
Groups = students.Remove(f => f.StudentID.Equals(studentID));
I get an error on this line: f => f.StudentID.Equals(studentID)
I am having difficulty from my previous posts here https://stackoverflow.com/questions/10116685/linq-deleted-users-still-associated-with-groups and here Delete method in WCF
So I thought maybe I could delete the students contained within groups but I get an error Cannot convert lambda expression to type Student because it is not a delegate type.
Just a quick up date as to what I am trying to do, lets say student A can belong to multiple Groups. Then if I delete that student the student should no longer list any groups. But it does. However if I search lets say Group Computing the student isnt there. Only seems to be when I take the student and search which groups he belongs to (even if he is deleted) it returns the group I originally added him to. THIS SHOULDNT HAPPEN ARGHH
If you are trying to pass in the predicate, you have to use the RemoveAll method. Remove method takes in a single element
var numRemoved = students.RemoveAll(f => f.StudentID == studentID);
Also the RemoveAll method doesn't return the "updated" list, it updates the list. So students would be updated list i.e. it would not have students with StudentID == studentID.
If you want to preserve the students as is, copy the elements to a new list
var newStudentList = new List<Student>(students);
and then filter that list
P.S. The common objects in both list are same i.e. if an object is updated in one list, it would get updated in the second list too. If a second copy is needed, deep cloning has to be implemented. This is just two lists from same pool of objects.
var numRemoved = newStudentList.RemoveAll(f => f.StudentID == studentID);
With your updates, it seems you are looking for
students.RemoveAll(s => s.StudentID == studentID);
Groups.ForEach(g => g.Groupsz.RemoveAll(gs => gs.StudentID == studentID));
Try with RemoveAll...........
List<T>.Remove() has a return type of bool. So you're not going to get a List<Group> from List<Student>.Remove().
You can use var removed = students.RemoveAll(f => f.StudentID.Equals(studentID)); to get a list of which students were removed from list.
But you'll need to do a little more to get that list of students to be a List<Group>. There is not enough information in your question to tell if a conversion possibility exists.
Related
I need help with filtering list data in c#.
I got 3 class named Product.cs, Storage.cs and Inventory.cs.
public class Storage{
string StorageId;
string Name;
}
public class Inventory{
string InventoryId;
string StorageId;
string ProductId;
}
I got the filled List<Storage> mStorages, List<Product> mProduct and List<Inventory> mInventories.
I have trouble to print mStorages that contain with specific productId that only can be obtained from mInventories.
So, I tried this:
List<Storage> mFilteredStorage;
for(int i=0;i<mStorages.Count;i++){
if(mStorages[i] contain (productId from inventories)){
mFilteredStorage.add(mstorages[i]);
}
So I can get mFilteredStorage that contains specific product from inventories. (in inventories there are lot of product id).
What should I do to get that filteredStorage? I tried to use list.contains() but it only return true and at last there are duplicated storage at mFilteredStorage.
Really need your help guys. Thanks in advance.
I suggest you to read about lambda-expressions, that is what you are looking for.
mFilteredStorage.AddRange(mStorages.Where(storage => inventories.Any(inventory => inventory.productId == storage.productId)).ToList());
This returns you a list with your filtered conditions. So right after Where you iterate over each item in your list, I called this item storage. (you can name those what ever you want to) Then we iterate over your object inventories with another lambda expression. This, the second lambda expression, returns either true if any of inventories's productIds match the productId of the current iterating object of mStorages or false if they don't match.
So you once the productIds match you can imagine the code like the following:
mStorages.Where(storage => true);
And once the result of the second lambda expression is true, storage will be added to the IEnumerable you will get as a result of the Where method.
Since we get an IEnumerable as return, but we want to add those Storage objects to mFilteredStorage, I convert the IEnumerable to a list, by:
/*(the return object we get from the `Where` method)*/.ToList();
You can use LINQ to accomplish your goal. Since Storage has no ProductId, the query will match by StorageId.
var filteredStoragesQry =
from storage in mStorages
where inventories.Any(inventory => inventory.StorageId == storage.StorageId)
select storage;
mFilteredStorages = filteredStoragesQry.ToList();
This query is for LINQ to objects, but it will also work in Entity Framework, when you replace mStorages and inventories by the respective DbSet objects from the context.
mStorages.Join(mInventories, x => x.StorageId, y => y.StorageId, (x, y) => new { Storage = x, ProductId = y.ProductId})
.Where(z => z.ProductId == "specificProductId").Select(z => z.Storage).ToList()
I ended with this code.
mFilteredStorage = tempStorage.GroupBy(s => s.Id).Select(group => group.First()).ToList()
This code is what I want to show.
The challenge is about converting from method chain to standard linq a piece of code full of group by.
The context
To fully understand the topic here you can read the original question (with class definitions, sample data and so on): Linq: rebuild hierarchical data from the flattened list
Thanks to #Akash Kava, I've found the solution to my problem.
Chain method formulation
var macroTabs = flattenedList
.GroupBy(x => x.IDMacroTab)
.Select((x) => new MacroTab
{
IDMacroTab = x.Key,
Tabs = x.GroupBy(t => t.IDTab)
.Select(tx => new Tab {
IDTab = tx.Key,
Slots = tx.Select(s => new Slot {
IDSlot = s.IDSlot
}).ToList()
}).ToList()
}).ToList();
But, for sake of knowledge, I've tried to convert the method chain to the standard Linq formulation but something is wrong.
What happens is similar to this..
My attempt to convert it to Linq standard syntax
var antiflatten = flattenedList
.GroupBy(x => x.IDMacroTab)
.Select(grouping => new MacroTab
{
IDMacroTab = grouping.Key,
Tabs = (from t in grouping
group grouping by t.IDTab
into group_tx
select new Tab
{
IDTab = group_tx.Key,
Slots = (from s in group_tx
from s1 in s
select new Slot
{
IDSlot = s1.IDSlot
}).ToList()
}).ToList()
});
The result in LinqPad
The classes and the sample data on NetFiddle:
https://dotnetfiddle.net/8mF1qI
This challenge helped me to understand what exactly returns a Linq Group By (and how prolix is the Linq syntax with Group By).
As LinqPad clearly shows a Group By returns a List of Groups. Group is a very simple class which has just one property: a Key
As this answer states, from definition of IGrouping (IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable) the only way to access to the content of the subgroups is to iterate through elements (a foreach, another group by, a select, ecc).
Here is shown the Linq syntax formulation of the method chain.
And here is the source code on Fiddle
But let's go on trying to see another solution:
What we usually do in SQL when we do a Group By is to list all the columns but the one which have been grouped. With Linq is different.. it still returns ALL the columns.
In this example we started with a dataset with 3 'columns' {IDMacroTab, IDTab, IDSlot}. We grouped for the first column, but Linq would return the whole dataset, unless we explicitly tell him..
I'm working on a groupby query using Linq, but I want to set the value for a new property in combination with another list. This is my code:
var result = list1.GroupBy(f => f.Name)
.ToList()
.Select(b => new Obj
{
ClientName = b.Name,
Status = (AnotherClass.List().Where(a=>a.state_id=b.????).First()).Status
})
I know I'm using a group by, but I'm not sure of how to access the value inside my bcollection to compare it with a.state_id.
This snippet:
Status = (AnotherClass.List().Where(a=>a.state_id=b.????).First()).Status
I've done that before but months ago I don't remember the syntax, when I put a dot behind b I have acces only to Key and the Linq Methods... What should be the syntax?`
Issue in your code is happening here:
a=>a.state_id=b.????
Why ?
Check type of b here, it would be IGrouping<TKey,TValue>, which is because, post GroupBy on an IEnumerable, you get result as IEnumerable<IGrouping<TKey,TValue>>
What does that mean?
Think of Grouping operation in the database, where when you GroupBy on a given Key, then remaining columns that are selected need an aggregation operation,since there could be more than one record per key and that needs to be represented
How it is represented in your code
Let's assume list1 has Type T objects
You grouped the data by Name property, which is part of Type T
There's no data projection so for a given key, it will aggregate the remaining data as IEnumerable<T>, as grouped values
Result is in the format IEnumerable<IGrouping<TK, TV>>, where TK is Name and TV represent IEnumerable<T>
Let's check out some code, break your original code in following parts
var result = list1.GroupBy(f => f.Name) - result will be of type IEnumerable<IGrouping<string,T>>, where list1 is IEnumerable<T>
On doing result.Select(b => ...), b is of type IGrouping<string,T>
Further you can run Linq queries on b, as follows:
b.Key, will give access to Name Key, there's no b.Value, for that your options could be following or any other relevant Linq operations:
a=>b.Any(x => a.state_id == x.state_id) or // Suuggests if an Id match in the Collection
a=>a.state_id == b.FirstOrDefault(x => x.state_id) //Selects First or default Value
Thus you can create a final result, from the IGrouping<string,T>, as per the logical requirement / use case
Is something like this possible? I am getting the below error.
db.SomeTable.Add(new SomeTable()
{
GuidId = Guid.NewGuid(),
Name = db.AnotherTable.Select(x => x.Name.Where(x.Id == localVariable.Id))
}
);
db.SaveChanges();
Unknown Select(?) of System Data Entity DbSet
Select returns an IEnumerable, not an individual record. You would needed to add a .First() call to grab just one record. Entity Framework thinks you're trying to put a list into a single field.
Furthermore, your use of Where() is incorrect. Where also returns an IEnumerable, and can only be applied on to an IEnumerable. Think of it as a way to filter a list.
Here's how to do what I think you're asking for:
Name = db.AnotherTable.First(x => x.id == someId).Name
I think what you want is this:
Name = db.AnotherTable
.First(x => x.Id == localVariable.Id)
.Name;
The steps of this are:
Go into list of items in AnotherTable
Find the first item where the Id of the item is equal to localVariable.Id
Set your variable equal to the Name property of the item you found
You can also use FirstOrDefault(), Single(), and SingleOrDefault().
I have a query I need to create to get a list of objects (Lets call them Cats) from a database stored remotely through Microsoft Azure on Windows Phone 8.
In order to get this list of objects (Cats) I have to iterate through a list of other objects to pull out the foreign key, lets call it a list of CatHomes. But I also want all cats called Bob.
I know how to get all Cats called Bob:
private async void getCatsForCatHomesOrCalledBob(Collection<CatHome> homes)
{
// get a list of cats that have visited the cat homes in the collection
IMobileServiceTableQuery<Cat> query = catTable
.Where(cat => cat.Name == "Bob");
Collection<Cat> items = null;
// add the items to a list
items = await query.ToCollectionAsync();
}
but how do I also iterate through the Collection of CatHome objects and add them to the query, to build it up?
I am aware that I could do something like:
IMobileServiceTableQuery<Cat> query =
catTable.Where(cat => cat.Name == "Bob" || cat.HomeId == 1
|| cat.HomeId == 2);
Which doesn't allow me to iterate through the Collection, what I want to do is something like:
IMobileServiceTableQuery<Cat> query = catTable.Where(cat => cat.Name == "Bob");
foreach(CatHome home in homes){
query.AddOrStatement(cat.HomeId == home.Id);
}
But cannot find a way to do this.
Is there an alternative? Ideally I would like to be able to pass a String variable to the Where(), so as I can build up the SQL statement as a String, but cannot find a means to do that either.
There is similar functionality for Android and iOS where you can build up the expression using .Or() or .And() methods, which aren't present in IMobileServiceTableQuery in WP8, and things like Skip(), which is present.
I am aware that I could iterate through the list of CatHome objects and do a query for each one, adding the results to a master list, but that seems like a bad use of the phone's resources.
Any help would be appreciated.
Assuming homesin foreach(CatHome home in homes){ is a list you can just do this:
IMobileServiceTableQuery<Cat> query = catTable.Where(cat => cat.Name == "Bob" && homes.Contains(cat.HomeId));
Not sure if the IEnumerable.Contains works to, but converting homes into a List<CatHome> should be easy.
Here it is already answered Linq query with Array in where clause?. Get the list of foreign keys first and then use it in contains where clause