Reputation: 621
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
Uncommenting Where clause groups only a and b==> All those items with empty value ( "" ) does not get included
Commenting Where clause groups a,b and "" string to make a list with 3 groups(a,b and "").
Upvotes: 2
Views: 16251
Reputation: 13488
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);
}
}
Upvotes: 0
Reputation: 30454
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();
}
Upvotes: 0
Reputation: 460098
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.
Upvotes: 8