2

I have a list
My Requirement

I need a LINQ lambda query to Group to a list if a Condition meets else do not group.

i.e On a condition I want it to be grouped
else it should not be grouped

I have searched net - I get details on grouping on condition and I couldn't get and understand on how the remaining item should be included without grouping.

Some info found on net was for grouping Conditionally - but with that those items not meeting conditions do not include in the resultant list.

For Example

List = [{1,"a"},{40,""),{9,"a"},{52,"b"),{2,"b"},{99,""),{88,"b"}]

The expected resultant list is to be grouped by a,b but "" should not be grouped

ResultantList = Group[0] ==> [{1,"a"}
                             {9,"a"}],
                 Group[1] ==>[ {52,"b"),
                               {2,"b"},
                               {88,"b"}] ,
                 // all other items which is "" should be included without groups
                Group[3] [ {40,""}]  
                 Group[4][ {99,""} ] 

What I have tried

var resultantList =  sigList
                    .GroupBy(s => s.SignalGroup)
                    .Select(grp => grp.ToList())
                     //.Where(g => !g.Any(grp => grp.SignalGroup == ""))
                     .ToList();

With the above as expected

  1. Uncommenting Where clause groups only a and b==> All those items with empty value ( "" ) does not get included

  2. Commenting Where clause groups a,b and "" string to make a list with 3 groups(a,b and "").

jAntoni
  • 591
  • 1
  • 12
  • 28

3 Answers3

8

Assuming that the first column is something like a unique Id:

var resultantList =  sigList
    .GroupBy(s => s.SignalGroup == "" ? s.Id.ToString() : s.SignalGroup)
    .Select(grp => grp.ToList())
    .ToList();

So if SignalGroup is an empty string the GroupBy takes the (unique) Id, in all other cases the SignalGroup, so you get the desired result of one group per Id if SignalGroup is "".

If it's not unique use Guid.NewGuid().ToString() as key for the group.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Also it will not work, at case of `Id` of element with empty property is equal to corresponding property of another element. – Slava Utesinov Sep 01 '17 at 15:45
  • @Slava Utesinov I din't get you. I have tried `var resultantList = sigList .GroupBy(s => s.SignalGroup) .Select(grp => grp.ToList()) .ToList(); ` and it works for me. ID is sure to have a value aways. Do you mean thta? – jAntoni Sep 03 '17 at 07:04
  • @jAntoni, try this solution with data like: `{SignalGroup:"", id:1}, {SignalGroup:"1", id:2}`. – Slava Utesinov Sep 04 '17 at 05:23
  • @Tim, If I want to make a group by more than 2 columns or fields then what's the condition? Say for instance: If the SignalGroup is "" then group by (column1 & column2 ) else if SignalGroup is not "" then group by (column1 & column2 &column3). I am able to group on multiple columns but it's expecting the column's count as same but in my scenario need to group by -> if then 2-columns else 3-columns like that. Is this possible in LINQ ? Please help me on this – Md Aslam Jul 15 '20 at 12:59
0

You can solve your problem this way, without redundant Id or Guid.NewGuid()at all:

public class Test
{
    public string property { get; set; }
    public string name { get; set; }
}

public static void Main()
{
    var items = new List<Test>()
    {
        new Test{ property = "1", name = "1" },
        new Test{ property = "1", name = "2" },
        new Test{ property = "2", name = "3" },
        new Test{ property = "2", name = "4" },
        new Test{ property = "", name = "5" },
        new Test{ property = "", name = "6" }
    };

    var result = items
            .GroupBy(x => x.property == "")
            .SelectMany(x =>
                !x.Key ?
                    x.GroupBy(y => y.property).Select(y => y.ToList()).ToList() 
                    :
                    x.Select(y => new List<Test> { y }).ToList()
                ).ToList();

    foreach (var res in result)
    {
        Console.WriteLine("Group " + res.First().property + ":");
        foreach (var item in res)
            Console.WriteLine(item.name);
    }
}
Slava Utesinov
  • 13,410
  • 2
  • 19
  • 26
0

In your example you specify that the items that you don't want to group have an empty string value.

A solution where you can specify which values should not be grouped:

static class EnumerableExtensions
{
    public static IEnumerable<IGrouping<TKey, TSource>> SpecialGroupBy(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        TKey dontGroupKeyValue)
    {
        // Group all items according to the keySelector,
        // return all groups, except the group with the key dontGroupKeyValue

        var groups = source.GroupBy(keySelector);
        foreach (IGrouping<TKey, TSource> group in groups)
        {
            if (group.Key != dontGroupKeyValue)
            {  // return this as a group:
               return group;
            }
            else
            {   // return every element of the group as a separate IGrouping
                foreach (TSource element in group)
                {
                   return Grouping.Create(dontGroupKeySelector, element)'
                }
             }
         }
    }
}

For this I use an adaptation of a helper class I found here in StackOverFlow. This implementation hides the actual implementation of the IGrouping. It only exposes the IGrouping required properties and functions to create it. If you do need to Create IGrouping regularly consider putting this in some base library.

public class Grouping<TKey, TElement> : IEnumerable<TElement>, IGrouping<TKey, TElement>
{
    public static IGrouping<TKey, TElement> Create(TKey key, TElement element)
    {
        return new Grouping<TKey, TElement>()
        {
            Key = key,
            Elements = new TElement[] {element},
        };
    }
    public static IGrouping<TKey, TElement> Create(TKey key,
        IEnumerable<TElement> elements)
    {
        return new Grouping<TKey, TElement>()
        {
            Key = key,
            elements = elements,
        };
    }

    private Grouping() { }

    public TKey Key { get; private set; }
    private IEnumerable<TElement> elements;

    public IEnumerator<TElement> GetEnumerator() => this.elements.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116