我有一个IGrouping
个序列
IEnumerable<IGrouping<Key, Element>> sequence = ...;
和谓词
Func<Element, bool> predicate = ...;
如何通过将结果保持为Element
而不使序列变平并完全重新创建分组来过滤IEnumerable<IGrouping<Key, Element>>
?
我可以做类似的事情
IEnumerable<IGrouping<Key, Element>> filtered =
from grouping in sequence
from element in grouping
where predicate(element)
select new { grouping.Key, element } into keyElem
group keyElem.element by keyElem.Key into g
select g;
但是,这取决于序列的大小和所涉及的类型,将需要一些时间和/或内存。
我想“简单地”过滤IGrouping
内的元素。我会在结果中接受空分组。
(我认为可以通过回答如何过滤IEnumerable<IEnumerable<Element>>
的元素同时将结果保留为IEnumerable<IEnumerable<Element>>
的问题来回答这个问题。)
答案 0 :(得分:2)
您无法修改现有的IGrouping
,但可以轻松创建自己的实现和帮助程序方法:
public static class Groupings
{
public static IGrouping<TKey, TElement> FilterElements(
this IGrouping<TKey, TValue> grouping,
Func<TValue, bool> predicate) =>
new LateGrouping<TKey, TValue>(grouping.Key, grouping.Where(predicate));
}
internal class LateGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
public TKey Key { get; }
private readonly IEnumerable<TElement> elements;
public LateGrouping(TKey key, IEnumerable<TElement> elements)
{
Key = key;
this.elements = elements;
}
public IEnumerator<TElement> GetEnumerator() => elements.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
然后,您只需拿起IEnumerable<IGrouping<...>>
并使用常规选择,在内部进行过滤:
groupings = groupings.Select(g => g.FilterElements(predicate));
答案 1 :(得分:0)
无法从IGrouping
删除对这个想法(How to remove an element from an IGrouping)至关重要的项目。
最漂亮的方法是在此之后删除Enities并删除空的IGroupings:
IEnumerable<IGrouping<string, object>> sequence = null;
Func<object, bool> predicate = null;
// Would be nice if this would work
//Parallel.ForEach(sequence, y => y.Remove(y.Where(z => predicate(z))); });
sequence = sequence.Where(s => s.Count() == 0);
因此我们需要为每个IGrouping
IEnumerable<IGrouping<string, object>> sequence = null;
Func<object, bool> predicate = null;
IEnumerable<IGrouping<string, object>> result2 = sequence.Select(s =>
{
var t = new MyGrouping<string, object>() { Key = s.Key };
t.AddRange(s.Where(w => predicate(w)));
return t;
}).Where(s => s.Count > 0);
但是您将需要此类:
public class MyGrouping<TKey, TElement> : List<TElement>, IGrouping<TKey, TElement>
{
public TKey Key
{
get;
set;
}
}
我喜欢简单的一个:
IEnumerable<IGrouping<string, object>> sequence = null;
Func<object, bool> predicate = null;
// Make it a List again
var tmpList = sequence.SelectMany(s => s.ToList().Select(l => new { key = s.Key, val = l }));
// Filter your List
var tmpFilteredList = tmpList.Where(l => predicate(l.val));
// Group it back again
IEnumerable<IGrouping<string, object>> result = tmpFilteredList.GroupBy(g => g.key, g => g.val);