I have List<IGrouping<string,string>>.
Is is somehow possible to add new item to this list? Or actually, is it possible to create some IGroup开发者_运维问答ing object?
If you really wanted to create your own IGrouping<TKey, TElement>, it is a simple interface to implement:
public class Grouping<TKey, TElement> : List<TElement>, IGrouping<TKey, TElement>
{
public Grouping(TKey key) : base() => Key = key;
public Grouping(TKey key, int capacity) : base(capacity) => Key = key;
public Grouping(TKey key, IEnumerable<TElement> collection)
: base(collection) => Key = key;
public TKey Key { get; }
}
Note: you shouldn't try to allow the
Keyto be settable, mainly because the key should be managed by the collection that this Grouping is contained within.
This class inherits from List<T> and implements the IGrouping interface. Aside of the requirement of being an IEnumerable and IEnumerable<TElement> (which List<T> satisfies) the only property to implement is Key.
You could create List of these groups from scratch:
var groups = new List<Grouping<string, string>>();
groups.Add(new Grouping<string,string>("a", new string [] { "apple" }));
groups.Add(new Grouping<string,string>("p", new string [] { "peach", "pear" }));
groups.Add(new Grouping<string,string>("o", new string [] { "orange" }));
// inline variant:
groups = new List<Grouping<string, string>>
{
new Grouping<string, string>("a", new string[] { "apple" }),
new Grouping<string, string>("p", new string[] { "peach", "pear" }),
new Grouping<string, string>("o", new string[] { "orange" }),
};
Or you could use this structure to append new groups to the results of a previous Linq GroupBy expression that has been evaluated into a list:
var strings = new string [] { "apple", "peach", "pear" };
var groups = strings.GroupBy(x => x.First().ToString()).ToList();
…
// Inject a new item to the list, without having to re-query
groups.Add(new Grouping<string,string>("o", new string [] { "orange" }));
If you need to add Items to the groups resolved from an IGrouping expression you can cast the Linq results into a List of Grouping:
var strings = new string [] { "apple", "peach", "orange" };
var groupExpression = strings.GroupBy(x => x.First().ToString());
var editableGroups = groupExpression.Select(x => new Grouping<string,string>(x.Key, x)).ToList();
…
// Add "pear" to the "p" list, with a check that the group exits first.
var pGroup = editableGroups.FirstOrDefault(x => x.Key == "p");
if (pGroup == null)
editableGroups.Add(new Grouping<string, string>("p", new string[] { "pear" }));
else
pGroup.Add("pear");
As of .NET 4.0, there do not appear to be any public types in the BCL that implement the IGrouping<TKey, TElement> interface, so you won't be able to 'new one up' with any ease.
Of course, there's nothing stopping you from:
- Creating a concrete type yourself that implements the interface, as @Nathan Anderson points out.
- Getting an instance / instances of
IGrouping<TKey, TElement>from a LINQ query such asToLookupandGroupByand adding it / them to your list. - Calling
ToList()on an existing sequence of groups (from ToLookup / GroupBy).
Example:
IEnumerable<Foo> foos = ..
var barsByFoo = foos.ToLookup(foo => foo.GetBar());
var listOfGroups = new List<IGrouping<Foo, Bar>>();
listOfGroups.Add(barsByFoo.First()); // a single group
listOfGroups.AddRange(barsByFoo.Take(3)); // multiple groups
It's not clear why you would want to do this, though.
IGrouping<TKey, TElement> CreateGroup<TKey, TElement>(IEnumerable<TElement> theSeqenceToGroup, TKey valueForKey)
{
return theSeqenceToGroup.GroupBy(stg => valueForKey).FirstOrDefault();
}
You can also hack the grouping by not grouping on something within the list:
var listOfGroups = new[] { "a1", "a2", "b1" }
.GroupBy(x => x.Substring(0, 1))
.ToList();
// baz is not anything to do with foo or bar yet we group on it
var newGroup = new[] { "foo", "bar" }.GroupBy(x => "baz").Single();
listOfGroups.Add(newGroup);
listOfGroups then contains:
a:
a1, a2
b:
b1
baz:
foo, bar
IDEOne example
Based on dovid's answer, I created the following extension method, that creates an instance of IGrouping<TKey, TElement> from an IEnumerable<TElement> and a key of type TKey:
public static IGrouping<TKey, TElement> ToGroup<TKey, TElement>(
this IEnumerable<TElement> elements,
TKey keyValue)
{
return elements.GroupBy(_ => keyValue).FirstOrDefault();
}
It can be called like this:
var fruits = new [] { "Apples", "Bananas" };
var myFruitsGroup = fruits.ToGroup("fruitsKey");
Beware that ToGroup() can return null.
I also created an additional GroupBy extension method:
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, IEnumerable<TElement>> elementsSelector)
{
return source
.Select(s => elementsSelector(s)
.ToGroup(keySelector(s)))
.Where(g => g != default(IGrouping<TKey, TElement>));
}
It can be used like this:
var foodItems = new []
{
new { Category = "Fruits", Items = new [] { "Apples", "Bananas" } },
new { Category = "Vegetables", Items = new [] { "Tomatoes", "Broccoli" } },
};
var categoryGroups = foodItems.GroupBy(i => i.Category, i => i.Items);
var headers = from header in new[] {
new { Name = "One", List = new[] { "One 1", "One 2", "One 2" } },
new { Name = "Two", List = new[] { "Two 1", "Two 2", "Two 2" } }
} from value in header.List group value by header.Name;
The IGrouping interface is for the GroupBy() operator in LINQ. You would normally get an IGrouping object from a LINQ query with a group by clause. It doesn't make much sense to have a list of groupings, though.
加载中,请稍侯......
精彩评论