How do I remove an object directly from an IGrouping IGrouping<DateTime, VMAppointment>?
The only way I know of currently is to generate a new IGrouping without the concering element, but I don't like this way because it causes some trouble within my application.
Any ideas?
No, there's no way to mutate an IGrouping<,>, at least in general - and even if you knew the concrete type, I don't believe any of the implementations exposed by the .NET framework allow the group to be mutated.
Presumably the grouping is the result of some query - so if possible, change the original query to exclude the values you aren't interested in.
I know this is old question, but hopefully this helps someone else. A workaround for this is to cast the group to a list, then use the values from the list instead of the group.
var groups = someList.GroupBy(x => x...);
foreach (var group in groups)
{
var groupList = group.ToList();
...
groupList.Remove(someItem);
//Process the other code from groupList.
}
You could cast using Select and use TakeWhile if you have a testable condition (such as null as in the example) on a property in your group:
var removedItemsList = group.Select(x => x.TakeWhile(t => t.someProperty != null));
This will return an IEnumerable<IEnumerable<YourGroup>>.
Related
So I wonder if I can compare two Lists without using foreach because the List is with a custom class. Inside the class, it contains two variables. one of them is called GUID. In order to access the GUID for List A, I use Any(x =>x.guid) And to access the same things in List B I have to do a foreach, which is like this, foreach(var x in List B){x.guid).
What I want to know is, is it possible to do it without the foreach? And if it is possible, how? I have been looking for an answer online but most of the example is looking at an item in one list. What I'm trying to do is compare one custom list to another, but only one variable inside the class instead of comparing the whole class.
The code below is how I do it, comparing one to another, but is there a more efficient way to do it
List<MySecondGameList> myloadinglist = JsonConvert.DeserializeObject<List<MySecondGameList>>(json);
foreach (var id in myloadinglist)
{
if (GameData_List.my_loading_list.Any(x => x.guid == id.guid))
{
Debug.Log("Matching!!!!!!!!!!!!!!!!");
continue;
}
GameData_List.my_loading_list.Add(id);
Debug.Log("It is loading");
}
}
You can hide loops within Linq queries, e.g.
List<MySecondGameList> myloadinglist = JsonConvert
.DeserializeObject<List<MySecondGameList>>(json);
// HashSet<T> provides faster Contains than List
HashSet<Guid> existing = GameData_List
.my_loading_list
.Select(item => item.guid)
.ToHashSet();
// We can put it compact with a help of AddRange instead of Add
GameData_List
.my_loading_list
.AddRange(myloadinglist.Where(item => !existing.Contains(item.guid)));
I have used the following linq query to access the member Names. The following successfully returns all member Name values within the collection.
var we = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "Name").Select(b => b.Value).ToList();
I want to now filter this down based another property within "Members" called AbsoluteUri which is nested in a property AgentsByUri. This doesnt work but gives an idea of the structure:
var uri = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "AgentsByUri").Select(b => b.Value).//??? I NEED TO NOW ACCESS "AbsoluteUri"
How can I combine these in to one query so that I can return only the "Names" that have an "AbsoluteUri" that contains "SomeValue". AbsoluteUri seems to be nested in a collection thats nested in AgentsByUri which adds to the complication.
You can see the structure of the AgentsByUri object here - Using C# Linq to query nested objects
Excuse my terminology, I'm reasonably new to C#! Hopefully this makes sense :)
Any help or guidance VERY appreciate :)
EDIT3
Getting somewhere! Casting as dynamic partially working - member.AgentsByUri is now OK, just cant figure out how to make it apply to the rest of the query. Tried adding in various locations by no luck.
EDIT2
Thanks for everyone's input. I haven't had any further success. I think the biggest problem is that I am dealing with a PowerShell object which is dynamically generated at run time. As a result I cannot access the classes/object because the compiler doesn't yet no about them. To get around this I use the "dynamic" type which allows the compiler to trust that what I provide will be valid at run time. Can I cast as dynamic in a linq query? Or do I need to go about this in a different way?
Heres what I get with the examples give:
EDIT1 (click and zoom, image is high res):
I'm just writing this down, based on the image you provided. However, I can not check for correctness without the surrounding code, so no guarantees.
var uri = CsQ.Groups
.SelectMany(g => g.Members)
.Where(m => m.AgentsByUri.SelectMany(a => a.Value, (a, v) => v.AbsoluteUri).Contains("SomeValue"))
.Select(b => b.Value)
In LINQ syntax it should be much clearer
var uri =
from group in CsQ.Groups
from dynamic member in group.Members
where
(from agent in (IEnumerable<dynamic>)member.AgentsByUri
where agent.Name = "AgentsByUri" // this line may be redundant
from x in (IEnumerable<dynamic>)agent.Value
select x.AbsoluteUri).Contains("SomeValue")
select member.Value;
EDIT: The suggestion in my second comment does not quite work. I changed the code in LINQ syntax above to account for dynamic objects as the source by explicitly casting to IEnumerable<dynamic>. Note however that the cast will fail for an enumeration of a value type.
I hope this will work for you.
It's hard to give a proper answer without having a proper overview of the class structure. I guess this might work:
var uri = CsQ.Groups.SelectMany(g => g.Members).Where(a => a.Name == "AgentsByUri").Select(b => b.Value).Where(x => x.AbsoluteUri == "SomeValue");
I had to do some digging and make a few assumptions here:
You want to return a list of PSMemberInfo.
PsMemberInfo.Name must equal "AgentsByUri".
In the Value property (which is a collection of Uri), you want to filter this collection on items whose AbsoluteUri property equals "SomeValue".
var we = CsQ.Groups.SelectMany(g => g.Members).Where(member => member.Name == "AgentsByUri" && member.Value != null && member.Value.Any(uri => uri.AbsoluteUri == "SomeValue")).ToList();
I like to think of myself as pretty good with LINQ, but every now and then I try to accomplish something that I just can't get to work. I'd like to convert a SPListItemCollection to a dictionary so I can use the key to look up a value without the need for a LINQ query each time:
var formsConfigItems = new Dictionary<string, string>();
foreach (SPListItem item in list.GetItems(query))
formsConfigItems.Add(item.Title, (item["Value"] == null ? string.Empty : item["Value"].ToString()));
This works, but I was hoping to do it in a cleaner fashion, using LINQ. (not a big deal but I like to use LINQ over for-loops whenever possible, although it's the same thing behind the scenes.
I tried to do something like this:
var formsConfigItems = (from SPListItem i in list.GetItems(query)
select new { i.Title, i["Value"].ToString() }).ToDictionary<string, string>(k=>k.Key, k=>k.Value);
But that doesn't seem to work. If I try to use a lambda expression on list.GetItems(query), I'm not given the option to use .Where or any LINQ commands (which is weird because it is an SPListCollection)
Thanks in advance.
Try:
var formsConfigItems = list.GetItems(query)
.Cast<SPListItem>()
.ToDictionary(item => item.Title,
item => Convert.ToString(item["Value"]));
To answer your queries:
If I try to use a lambda expression on list.GetItems(query), I'm not
given the option to use .Where or any linq commands (which is weird
because it is an SPListCollection)
That's because SPListCollection is an "old-school" collection that implements IEnumerable but not IEnumerable<T>, so C# / LINQ (at compile-time anyway) can't tell what type of items it contains. The Cast<SPListItem>() call helps work around this issue - it turns an IEnumerable into an IEnumerable<T>, allowing the type-algebra to work out at compile-time . Your for loop doesn't have this issue since you explicitly specify the type of the loop variable - the compiler inserts a cast on your behalf for each item in the sequence.
I tried to do something like this (query expression). But that
doesn't seem to work.
That's because you are not constructing the anonymous type instance correctly (property names can't be inferred for arbitrary expressions) and your lambda expression isn't quite right either (the property names you use don't match the property names of the anonymous type). Try this instead:
var formsConfigItems = (from SPListItem i in list.GetItems(query)
select new
{
i.Title,
Value = Convert.ToString(i["Value"])
}).ToDictionary(a => a.Title, a => a.Value);
Ani's got the better solution IMO, but one other thing you're missing: Your LINQ statement is creating a collection of anonymous items, but you're not giving names to the properties in that anonymous class.
The k=>k.Key expression doesn't work, because it doesn't know what Key is - you've only defined Title (since you didn't give it a name, it borrowed the one from the object). The Value one can't be automatically figured out, so it would throw a compiler error.
To do it this way, you'd need to specifically declare the names:
new { Key = i.Title, Value = i["Value"].ToString() }
Think this is a very basic question, but it's my first LINQ query and I'm completely stuck:
I have a dictionary with string key and list value (see definition below) and want to pull out elements of a list of a particular type having selected the list by the dictionary key.
IDictionary<string, IList<MyBaseType>> dataItemMap;
Where MySubType extends MyBaseType.
My dodgy query is:
string identCode = "foo";
IEnumerable<MySubType> query =
from entry in dataItemMap
where entry.Key == identCode
select entry.Value.OfType<MySubType>();
And the error message (from LinqPad):
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<MySubType>>'
to 'System.Collections.Generic.IEnumerable<MySubType>'.
An explicit conversion exists (are you missing a cast?)
The problem is clearly in the entry.Value.OfType<> but how can I specify the lists elements? I'm looking for something like entry.Value.Items.OfType<> ?
thanks.
I think you want something like this:
IEnumberable<MySubType> query = dataItemMap[identCode].OfType<MySubType>();
This will get the list with the given key, and then filter it to return only MySubType elements.
EDIT: I've been focusing on why the existing solution didn't work (and the general problem of "I've got a list of values for each element, and I want to flatten it") rather than taking a step back. As Andy's answer shows, you should almost certainly use the fact that it's a dictionary - turning it from an O(n) operation to O(1) :)
Two caveats:
Your current code will always perform an ordinal, culture-insensitive comparison with identCode and the dictionary keys; using the dictionary lookup will use whatever comparer it was constructed with.
Your current code will return an empty sequence if identCode isn't found in the dictionary; the dictionary indexer will throw an exception. You can use TryGetValue if you want to avoid that.
Note that if you know that all the elements in the last you're picking are actually of the right type, it would probably be better to use Cast than OfType:
var query = dataItemMap[identCode].Cast<MySubType>();
I generally prefer Cast to OfType when both would work, as it means that if my assumptions about the data in the sequence prove incorrect, I find out about it with an exception rather than silently missing data.
Note that Cast will also return null elements, whereas OfType won't.
No, the problem isn't in using OfType<> - it's that you've ended up with a sequence of sequences, but you're trying to assign that to a single sequence.
Either change the return type, or use another from clause to flatten the results:
IEnumerable<MySubType> query = from entry in dataItemMap
where entry.Key == identCode
from value in entry.Value.OfType<MySubType>()
select value;
I'd be tempted to use the extension methods directly:
var query = dataItemMap.Where(e => e.Key == identCode)
.SelectMany(e => e.Value.OfType<MySubType>());
I'd like to use the Uber-Coolness of LINQ set operations to express the following :
foreach (Group group in groups)
{
if (user.Groups.Contains(group))
{
assignedGroups.Add(group);
}
else
{
availableGroups.Add(group);
}
}
I thought it should be a two-liner achieving this :
var assigned = user.Groups.Intersect(groups);
var available = groups.Except(user.Groups);
Whenever I run this example the foreach approach fills my lists correctly, while the set operations result in an empty assigned list and a filled available list.
I thought it must be a problem concerning the equality check, but the fact that Contains() is working proves this wrong.
Can anyone help me see my misconception here?
the IEnumerable groups is also result of a LINQ query, just in case that information is of some help...
Well, it shouldn't make a difference, but from the point of view of symmetry I'd reverse how you're creating assigned. I'd also make sure that the query is only executed once, and that the remaining operations occur in-process:
var cachedGroups = groups.ToList();
var assigned = cachedGroups.Intersect(user.Groups);
var available = cachedGroups.Except(user.Groups);
One possibility is that user.Groups has a custom equality comparer. That would explain why the foreach version worked but the LINQ version didn't. What's the type of user.Groups, and how much do you know about the equality comparer it's using?