admin管理员组

文章数量:1384812

I take data from a datasource and group it for display. I after that, I wish to allow ad hoc filtering of the rows.

Does Linq allow me to do this without having to re-do the grouping a second time and without having to create my own implementation of IGrouping ?

The query I'd like to write is something like this:

/* for instance 
   var source = new string[]{ "Aa", "Ab", "B", "Bb", "C"  };
   Func<string,bool> filter= s => Regex.IsMatch(s, "B");
   var allGroups= source.GroupBy(s => s[0]);
*/


var filteredGroups= allGroups
            .Select(g=> (g.Key, g.Where(filter)))
            // Cast to an IGrouping here
            .Where(g => g.Count() > 0);

But the ValueTuple created in the Select isn't an IGrouping so that's not quite what I want.

The way I can see that doesn't involved re-grouping is to create my own implementation of IGrouping. (Arguably this is sensible: the class would assume, not guarantee, that the grouping is consistent.)

I take data from a datasource and group it for display. I after that, I wish to allow ad hoc filtering of the rows.

Does Linq allow me to do this without having to re-do the grouping a second time and without having to create my own implementation of IGrouping ?

The query I'd like to write is something like this:

/* for instance 
   var source = new string[]{ "Aa", "Ab", "B", "Bb", "C"  };
   Func<string,bool> filter= s => Regex.IsMatch(s, "B");
   var allGroups= source.GroupBy(s => s[0]);
*/


var filteredGroups= allGroups
            .Select(g=> (g.Key, g.Where(filter)))
            // Cast to an IGrouping here
            .Where(g => g.Count() > 0);

But the ValueTuple created in the Select isn't an IGrouping so that's not quite what I want.

The way I can see that doesn't involved re-grouping is to create my own implementation of IGrouping. (Arguably this is sensible: the class would assume, not guarantee, that the grouping is consistent.)

Share Improve this question edited Mar 23 at 13:17 Michał Turczyn 37.9k18 gold badges55 silver badges82 bronze badges asked Mar 18 at 13:23 Chris F CarrollChris F Carroll 12.4k3 gold badges60 silver badges68 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Well, you can write very simple implementation of IGrouping:

public class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
    private readonly IEnumerable<TElement> _elements;

    public Grouping(TKey key, IEnumerable<TElement> elements)
    {
        Key = key;
        _elements = elements;
    }

    public TKey Key { get; }

    public IEnumerator<TElement> GetEnumerator() => _elements.GetEnumerator();

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

And then you can define some global variable (private field), to hold grouped result:

var testList = new[]
{ 
    (Id: 1, Name: "foo"),
    (Id: 1, Name: "bar"),
    (Id: 2, Name: "foo"),
    (Id: 2, Name: "bar")
};

var grouped = testList.GroupBy(x => x.Id).ToArray();

And implement method that would accept just the fiter, allowing you to filter groups, without requiring reqgrouping the result:

static IEnumerable<IGrouping<TKey, TElement>> Filter<TKey, TElement>(
    IGrouping<TKey, TElement>[] grouped,
    Func<TElement, bool> filter)
{
    return grouped
        .Select(g => new Grouping<TKey, TElement>(g.Key, g.Where(filter)))
        .Where(g => g.Any()); // Remove empty groups
}

Putting it all together, here's sample console app:

var testList = new[]
{ 
    (Id: 1, Name: "foo"),
    (Id: 1, Name: "bar"),
    (Id: 2, Name: "foo"),
    (Id: 2, Name: "bar")
};

var grouped = testList.GroupBy(x => x.Id).ToArray();
var foos = Filter(grouped, item => item.Name == "foo");
var bars = Filter(grouped, item => item.Name == "bar");

本文标签: cFilter the detail rows of an already grouped list without redoing the groupingStack Overflow