Related
the expected result is new list with last occurrence order.
Is it possible using the group by. Please do helpful
var list = new List<string> {"name1", "name2", "name1", "name3", "name4", "name2", "name3"};
var result = list
.Select((value, index) => (value, index))
.GroupBy(x => x.value)
.Select(g => g.Last());
Obj obj0 = new() { Id = 0, Name = "name1" };
Obj obj1 = new() { Id = 1, Name = "name2" };
Obj obj2 = new() { Id = 2, Name = "name1" };
Obj obj3 = new() { Id = 3, Name = "name3" };
Obj obj4 = new() { Id = 4, Name = "name4" };
Obj obj5 = new() { Id = 5, Name = "name2" };
Obj obj6 = new() { Id = 6, Name = "name3" };
List<Obj> list = new() { obj0,obj1,obj2, obj3 , obj4 , obj5 , obj6};
var result = list.OrderByDescending(o => o.Id).DistinctBy(p => p.Name).OrderBy(o => o.Id);
foreach (Obj obj in result)
{
Console.WriteLine(obj);
}
record Obj
{
public int Id { get; set; }
public string Name { get; set; }
}
I have many fields which I would like to distinct generically.
Right now, I have to write this for each field:
if (cFieldName == "cEVEN_DAL")
{
eventLOGS_DistinctField = eventLogs.DistinctBy(x => x.cEVEN_DAL);
}
What I would like to do is something like:
eventLOGS_DistinctField = eventLogs.DistinctBy(myFieldName);
Is it possible to do this?
You can generate an Expression x => x.cEVEN_DAL with Linq API:
// because you said in comments that all fields are string I'm using Func<T, string> here
public static Expression<Func<T, string>> Create<T>(string fieldName)
{
var parameter = Expression.Parameter(typeof(T), "p");
var property = Expression.PropertyOrField(parameter, fieldName);
return Expression.Lambda<Func<T, string>>(property, parameter);
}
If you are usinq MoreLinq you need to compile this expression:
var lambda = Create< TypeOfEventLogItem >("cEVEN_DAL");
var func = lambda.Compile();
var result = eventLogs.DistinctBy(func);
This way:
class Program
{
static void Main(string[] args)
{
List<Test> tests = new List<Test>() //Example objects
{
new Test
{
A = 1,
B = 2,
C = 3,
},
new Test
{
A = 2,
B = 2,
C = 3,
},
new Test
{
A = 3,
B = 2,
C = 3,
},
new Test
{
A = 1,
B = 1,
C = 3,
},
new Test
{
A = 1,
B = 2,
C = 3,
},
new Test
{
A = 1,
B = 3,
C = 3,
},
new Test
{
A = 1,
B = 2,
C = 1,
},
new Test
{
A = 1,
B = 2,
C = 2,
},
new Test
{
A = 1,
B = 2,
C = 3,
}
};
List<Test> results = DistinctBy(tests, "A"); //Use of DistinctBy
}
private static List<T> DistinctBy<T>(List<T> objs, string propertyName)
{
Type type = typeof(T);
PropertyInfo property = type.GetProperty(propertyName);
return objs.GroupBy(x => property.GetValue(x)).Select(x => x.First()).ToList();
}
}
public class Test
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
I would like to break the KeySelector function of GroupBy into its own method. The KeySelector code isn't right. One major problem is Widget.foo isn't matching on "Green" but I think it should be.
Widget.cs
public class Widget
{
public string foo { get; set; }
public double bar { get; set; }
public bool fee { get; set; }
}
Program.cs
static void Main(string[] args)
{
var widgets = new List<Widget>()
{
new Widget() { foo = "red" , bar = 1.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = false },
new Widget() { foo = "green", bar = 3.0, fee = false },
new Widget() { foo = "blue" , bar = 4.0, fee = true }
};
var gb = widgets.GroupBy(
w => GenerateGroupByKey(),
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
}
KeySelector
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return new { widget.foo };
};
return s;
}
You must pass delegate to parameters, so just call the function and return the delegate.
var gb = widgets.GroupBy(
GenerateGroupByKey(),
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
Group by you using have parameters like below
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
Func<TKey, IEnumerable<TElement>, TResult> resultSelector
when you are writing w => GenerateGroupByKey()
you are creating a new FUNC with input parameter as GenerateGroupByKey()
But GenerateGroupByKey() itself is a FUNC
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return new { widget.foo };
};
return s;
}
And you are not invoking that FUNC
Solution:
Do not create another FUNC. Pass GenerateGroupByKey() directly :)
static void Main(string[] args)
{
var widgets = new List<Widget>()
{
new Widget() { foo = "red" , bar = 1.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = false },
new Widget() { foo = "green", bar = 3.0, fee = false },
new Widget() { foo = "blue" , bar = 4.0, fee = true }
};
var gb = widgets.GroupBy(
GenerateGroupByKey(), // Your FUNC method
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
}
Your grouping does not work because inside GenerateGroupByKey() method you create new object.
Objects are equals if they have equal memory references. You can review more details about Equals here.
For what you create new object like new { widget.foo }?
You can just return string instead of object.
Try to use delegate like it:
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return widget.foo;
};
return s;
}
Update: You can skip the second part of this question as George already helped answer the first part.
Part I: I am trying to convert the LINQ below
childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
to its equivalent Expression but I can't get Step 6 to produce a result. I also don't think I am using Expression.Constant in Steps 2 - 6 as appropriate but it seems to be working, for now at least.
Any help in fixing this would be much appreciated!
public class Parent
{
public Guid ID { get; set; }
public string Name { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public Guid ParentID { get; set; }
public Guid ID { get; set; }
public string Name { get; set; }
}
public void SO_Question()
{
Parent parentItem = new Parent() { ID = Guid.NewGuid(), Name = "Parent" };
parentItem.Children = new List<Child>();
List<Child> childItems = new List<Child>() { new Child() { ParentID = parentItem.ID, ID = Guid.NewGuid(), Name = "Child 1" },
new Child() { ParentID = parentItem.ID, ID = Guid.NewGuid(), Name = "Child 2" },
new Child() { ParentID = Guid.NewGuid(),ID = Guid.NewGuid(), Name = "Child 3" }
};
// Linq query that I am trying to write using Expressions
childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
System.Diagnostics.Debug.WriteLine("Children count from LINQ: " + parentItem.Children.Count);
parentItem.Children.Clear();
Type parentEntityType = parentItem.GetType();
Type childEntityCollType= childItems.GetType();
Type childEntityType = childEntityCollType.GetGenericArguments()[0];
//1. (x => x.ParentID == parentItem.ID)
ParameterExpression predParam = Expression.Parameter(childEntityType, "x");
Expression left = Expression.Property(predParam, childEntityType.GetProperty("ParentID"));
Expression right = Expression.Property(Expression.Constant(parentItem), "ID");
Expression equality = Expression.Equal(left, right);
LambdaExpression le = Expression.Lambda(equality, new ParameterExpression[] { predParam });
//2. childItems.Where(x => x.ParentID == parentItem.ID)
Expression targetConstant = Expression.Constant(childItems, childEntityCollType);
Expression whereBody = Expression.Call(typeof(Enumerable), "Where", new Type[] { childEntityType }, targetConstant, le);
Func<IEnumerable> whereLambda = Expression.Lambda<Func<IEnumerable>>(whereBody).Compile();
object whereResult = whereLambda.Invoke();
//3. childItems.Where(x => x.ParentID == parentItem.ID).ToList()
Expression toListConstant = Expression.Constant(whereResult, whereResult.GetType());
Expression toListBody = Expression.Call(typeof(Enumerable), "ToList", new Type[] { childEntityType }, toListConstant);
Func<IEnumerable> listLambda= Expression.Lambda<Func<IEnumerable>>(toListBody).Compile();
object toListResult = listLambda.Invoke();
//5. (y => parentItem.Children.Add(y))
ParameterExpression feParam = Expression.Parameter(childEntityType, "y");
Expression addConst = Expression.Constant(parentItem, parentEntityType);
Expression childAccessor = Expression.Property(addConst, parentEntityType.GetProperty("Children"));
Expression body = Expression.Call(childAccessor, "Add", null, feParam);
LambdaExpression exp2 = Expression.Lambda(body, new ParameterExpression[] { feParam });
//6. childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
Expression targetConst2 = Expression.Constant(toListResult, toListResult.GetType());
Expression whereBody2 = Expression.Call(targetConst2, toListResult.GetType().GetMethod("ForEach"), exp2);
Delegate whereLambda2 = Expression.Lambda(whereBody2, feParam).Compile();
whereLambda.Invoke();
System.Diagnostics.Debug.WriteLine("Children count from Expressions: " + parentItem.Children.Count);
}
Part II: After #George fixed my issue I have hit a performance problem. I need to run the two lambdas thousands of times within a loop and currently it is very slow, probably because the expression tree is generated every time. How can I get around this?
I've restructured the code and simplified it a bit from my initial question.
In the code below parentItem is captured in the closure. How can i rewrite this so that I can use the expression from 1, 2 and 3 and only supply parentItem (on 2 and 3) as a variable to the lambda on every run?
public class Parent
{
public Guid ID { get; set; }
public string Name { get; set; }
public ICollection<Child> Children { get; set; }
public SingleChild SingleChild { get; set; }
}
public class Child
{
public Guid ParentID { get; set; }
public Guid ID { get; set; }
public string Name { get; set; }
}
public class SingleChild
{
public Guid ParentID { get; set; }
public Guid ID { get; set; }
public string Name { get; set; }
}
public static void SO_Question2()
{
Parent newParentItem1 = new Parent() { ID = Guid.NewGuid(), Name = "Parent1" };
Parent newParentItem2 = new Parent() { ID = Guid.NewGuid(), Name = "Parent2" };
Parent newParentItem3 = new Parent() { ID = Guid.NewGuid(), Name = "Parent3" };
newParentItem1.Children = new List<Child>();
newParentItem2.Children = new List<Child>();
newParentItem3.Children = new List<Child>();
List<Child> childItems = new List<Child>() {
new Child() { ParentID = newParentItem1.ID, ID = Guid.NewGuid(), Name = "Child 1" },
new Child() { ParentID = newParentItem1.ID, ID = Guid.NewGuid(), Name = "Child 2" },
new Child() { ParentID = newParentItem2.ID, ID = Guid.NewGuid(), Name = "Child 3" },
new Child() { ParentID = newParentItem2.ID, ID = Guid.NewGuid(), Name = "Child 4" },
new Child() { ParentID = newParentItem2.ID, ID = Guid.NewGuid(), Name = "Child 5" },
new Child() { ParentID = newParentItem2.ID, ID = Guid.NewGuid(), Name = "Child 6" },
new Child() { ParentID = newParentItem2.ID, ID = Guid.NewGuid(), Name = "Child 7" }
};
List<Parent> parentCollection = new List<Parent>() { newParentItem1, newParentItem2, newParentItem3 }; // In reality this can be a collection of over 2000 items
// Linq query that I am trying to write using Expressions
foreach (Parent parentItem in parentCollection)
{
parentItem.Children.Clear();
childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
}
System.Diagnostics.Debug.WriteLine("Children count from LINQ: " + newParentItem1.Children.Count);
newParentItem1.Children.Clear();
newParentItem2.Children.Clear();
newParentItem3.Children.Clear();
Type parentEntityType = parentCollection.GetType().GetGenericArguments()[0];
Type childEntityCollType = childItems.GetType();
Type childEntityType = childEntityCollType.GetGenericArguments()[0];
Parent parentVariable = parentCollection.First();
// 1. parentItem.Children.Clear()
var childCollection = Expression.Property(Expression.Constant(parentVariable), "Children");
Expression clearBody = Expression.Call(childCollection, typeof(ICollection<Child>).GetMethod("Clear"));
Expression<System.Action> bodyLambda = Expression.Lambda<System.Action>(clearBody);
System.Action compiledClear = bodyLambda.Compile();
// How can I change 1 and 2 so that they are not recreated with every iteration?
// My problem is that parentItem changes but is captured in the closure
//2. (x => x.ParentID == parentItem.ID)
ParameterExpression predParam = Expression.Parameter(childEntityType, "x");
Expression left = Expression.Property(predParam, childEntityType.GetProperty("ParentID"));
Expression right = Expression.Property(Expression.Constant(parentVariable), "ID");
Expression equality = Expression.Equal(left, right);
Expression<Func<Child, bool>> le = Expression.Lambda<Func<Child, bool>>(equality, new ParameterExpression[] { predParam });
Func<Child, bool> compileLambda = le.Compile();
//3. (y => parentItem.Children.Add(y))
ParameterExpression feParam = Expression.Parameter(childEntityType, "y");
Expression addConst = Expression.Constant(parentVariable, parentEntityType);
Expression childAccessor = Expression.Property(addConst, parentEntityType.GetProperty("Children"));
Expression body = Expression.Call(childAccessor, "Add", null, feParam);
Expression<Action<Child>> exp2 = Expression.Lambda<Action<Child>>(body, new ParameterExpression[] { feParam });
Action<Child> compileExp2 = exp2.Compile();
foreach (Parent parentItem in parentCollection)
{
parentVariable = parentItem;
compiledClear();
childItems.Where(compileLambda).ToList().ForEach(compileExp2);
}
System.Diagnostics.Debug.WriteLine("Children count from Expressions: " + parentCollection.First().Children.Count);
}
public static void SO_Question()
{
Parent parentItem = new Parent() { ID = Guid.NewGuid(), Name = "Parent" };
parentItem.Children = new List<Child>();
List<Child> childItems = new List<Child>() {
new Child() { ParentID = parentItem.ID, ID = Guid.NewGuid(), Name = "Child 1" },
new Child() { ParentID = parentItem.ID, ID = Guid.NewGuid(), Name = "Child 2" },
new Child() { ParentID = Guid.NewGuid(),ID = Guid.NewGuid(), Name = "Child 3" }
};
// Linq query that I am trying to write using Expressions
childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
System.Diagnostics.Debug.WriteLine("Children count from LINQ: " + parentItem.Children.Count);
parentItem.Children.Clear();
Type parentEntityType = parentItem.GetType();
Type childEntityCollType = childItems.GetType();
Type childEntityType = childEntityCollType.GetGenericArguments()[0];
//1. (x => x.ParentID == parentItem.ID)
ParameterExpression predParam = Expression.Parameter(childEntityType, "x");
Expression left = Expression.Property(predParam, childEntityType.GetProperty("ParentID"));
Expression right = Expression.Property(Expression.Constant(parentItem), "ID");
Expression equality = Expression.Equal(left, right);
LambdaExpression le = Expression.Lambda(equality, new ParameterExpression[] { predParam });
//2. childItems.Where(x => x.ParentID == parentItem.ID)
Expression targetConstant = Expression.Constant(childItems, childEntityCollType);
Expression whereBody = Expression.Call(typeof(Enumerable), "Where", new Type[] { childEntityType }, targetConstant, le);
Func<IEnumerable> whereLambda = Expression.Lambda<Func<IEnumerable>>(whereBody).Compile();
object whereResult = whereLambda.Invoke();
//3. childItems.Where(x => x.ParentID == parentItem.ID).ToList()
Expression toListConstant = Expression.Constant(whereResult, whereResult.GetType());
Expression toListBody = Expression.Call(typeof(Enumerable), "ToList", new Type[] { childEntityType }, toListConstant);
Func<IEnumerable> listLambda = Expression.Lambda<Func<IEnumerable>>(toListBody).Compile();
object toListResult = listLambda.Invoke();
//5. (y => parentItem.Children.Add(y))
ParameterExpression feParam = Expression.Parameter(childEntityType, "y");
Expression addConst = Expression.Constant(parentItem, parentEntityType);
Expression childAccessor = Expression.Property(addConst, parentEntityType.GetProperty("Children"));
Expression body = Expression.Call(childAccessor, "Add", null, feParam);
LambdaExpression exp2 = Expression.Lambda(body, new ParameterExpression[] { feParam });
//6. childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
Expression targetConst2 = Expression.Constant(toListResult, toListResult.GetType());
Expression whereBody2 = Expression.Call(targetConst2, toListResult.GetType().GetMethod("ForEach"), exp2);
Delegate d = Expression.Lambda(whereBody2).Compile();
d.DynamicInvoke();
System.Diagnostics.Debug.WriteLine("Children count from Expressions: " + parentItem.Children.Count);
}
For Question 2 only:
You could make the parent a variable to the lambda. However, that wouldn't mirror your LINQ code.
The alternative is to make a dummy variable, build your expression against the dummy variable, then your loop will set the dummy variable. I commented out the first lambda because it wasn't compiling and doesn't seem relevant to this question:
public static void SO_Question2()
{
Parent newParentItem = new Parent() { ID = Guid.NewGuid(), Name = "Parent" };
newParentItem.Children = new List<Child>();
List<Child> childItems = new List<Child>() {
new Child() { ParentID = newParentItem.ID, ID = Guid.NewGuid(), Name = "Child 1" },
new Child() { ParentID = newParentItem.ID, ID = Guid.NewGuid(), Name = "Child 2" },
new Child() { ParentID = Guid.NewGuid(),ID = Guid.NewGuid(), Name = "Child 3" }
};
List<Parent> parentCollection = new List<Parent>() { newParentItem }; // In reality this can be a collection of over 2000 items
// Linq query that I am trying to write using Expressions
foreach (Parent parentItem in parentCollection)
{
childItems.Where(x => x.ParentID == parentItem.ID).ToList().ForEach(y => parentItem.Children.Add(y));
}
System.Diagnostics.Debug.WriteLine("Children count from LINQ: " + newParentItem.Children.Count);
newParentItem.Children.Clear();
Type parentEntityType = parentCollection.GetType().GetGenericArguments()[0];
Type childEntityCollType = childItems.GetType();
Type childEntityType = childEntityCollType.GetGenericArguments()[0];
Parent parentVariable = parentCollection.First();
// 1. parentItem.Children.Clear()
//var childCollection = Expression.Property(Expression.Constant(parentVariable), "Children");
//Expression clearBody = Expression.Call(childCollection, typeof(List<Child>).GetMethod("Clear"));
//Delegate bodyLambda = Expression.Lambda(clearBody).Compile();
//bodyLambda.DynamicInvoke();
// How can I change 2 and 3 so that they are not recreated with every iteration?
// My problem is that parentItem changes but is captured in the closure
//2. (x => x.ParentID == parentItem.ID)
ParameterExpression predParam = Expression.Parameter(childEntityType, "x");
Expression left = Expression.Property(predParam, childEntityType.GetProperty("ParentID"));
Expression right = Expression.Property(Expression.Constant(parentVariable), "ID");
Expression equality = Expression.Equal(left, right);
Expression<Func<Child, bool>> le = Expression.Lambda<Func<Child, bool>>(equality, new ParameterExpression[] { predParam });
Func<Child, bool> compileLambda = le.Compile();
//3. (y => parentItem.Children.Add(y))
ParameterExpression feParam = Expression.Parameter(childEntityType, "y");
Expression addConst = Expression.Constant(parentVariable, parentEntityType);
Expression childAccessor = Expression.Property(addConst, parentEntityType.GetProperty("Children"));
Expression body = Expression.Call(childAccessor, "Add", null, feParam);
Expression<Action<Child>> exp2 = Expression.Lambda<Action<Child>>(body, new ParameterExpression[] { feParam });
Action<Child> compileExp2 = exp2.Compile();
foreach (Parent parentItem in parentCollection)
{
parentVariable = parentItem;
childItems.Where(compileLambda).ToList().ForEach(compileExp2);
}
System.Diagnostics.Debug.WriteLine("Children count from Expressions: " + parentCollection.First().Children.Count);
}
Notice that the compile (the expensive part) runs only once. The execution runs multiple times. This works because Expression.Constant(parentVariable) checks every time for whatever is in that reference location. Change the reference, you change the variable. Also note that parentVariable can be static, public, whatever. It doesn't have to be a local.
Have a collection of objects. Schematically:
[
{ A = 1, B = 1 }
{ A = 1, B = 2 }
{ A = 2, B = 3 }
{ A = 2, B = 4 }
{ A = 1, B = 5 }
{ A = 3, B = 6 }
]
Need:
[
{ A = 1, Bs = [ 1, 2 ] }
{ A = 2, Bs = [ 3, 4 ] }
{ A = 1, Bs = [ 5 ] }
{ A = 3, Bs = [ 6 ] }
]
Is it possible to LINQ such?
Note: Ordering is important. So Bs = [5] can't be merged with Bs = [1, 2]
Given these simplistic classes:
class C {
public int A;
public int B;
}
class R {
public int A;
public List<int> Bs = new List<int>();
}
You can do it like this:
var cs = new C[] {
new C() { A = 1, B = 1 },
new C() { A = 1, B = 2 },
new C() { A = 2, B = 3 },
new C() { A = 2, B = 4 },
new C() { A = 1, B = 5 },
new C() { A = 3, B = 6 }
};
var rs = cs.
OrderBy(o => o.B).
ThenBy(o => o.A).
Aggregate(new List<R>(), (l, o) => {
if (l.Count > 0 && l.Last().A == o.A) {
l.Last().Bs.Add(o.B);
}
else {
l.Add(new R { A = o.A, Bs = { o.B } });
}
return l;
});
Note: In the above I assume that the Bs and then the As have to be sorted. If that's not the case, it's a simple matter of removing the sorting instructions:
var rs = cs.
Aggregate(new List<R>(), (l, o) => {
if (l.Count > 0 && l.Last().A == o.A) {
l.Last().Bs.Add(o.B);
}
else {
l.Add(new R { A = o.A, Bs = { o.B } });
}
return l;
});
So basically you want to group together what has the same A-value and is consecutive.
You need to tranform the list of objects to an anonymous type which contains the previous/next element. I've used two Selects to make it more redable. Then you need to check if the two elements are consecutive(adjacent indices).
Now you have all you need to GroupBy, the value and the bool.
Your objects:
var list = new System.Collections.Generic.List<Foo>(){
new Foo(){ A = 1, B = 1 },
new Foo(){ A = 1, B = 2 },
new Foo(){ A = 2, B = 3 },
new Foo(){ A = 2, B = 4 },
new Foo(){ A = 1, B = 5 },
new Foo(){ A = 3, B = 6 }
};
The query:
var groups = list
.Select((f, i) => new
{
Obj = f,
Next = list.ElementAtOrDefault(i + 1),
Prev = list.ElementAtOrDefault(i - 1)
})
.Select(x => new
{
A = x.Obj.A,
x.Obj,
Consecutive = (x.Next != null && x.Next.A == x.Obj.A)
|| (x.Prev != null && x.Prev.A == x.Obj.A)
})
.GroupBy(x => new { x.Consecutive, x.A });
Output the result:
foreach (var abGroup in groups)
{
int aKey = abGroup.Key.A;
var bList = string.Join(",", abGroup.Select(x => x.Obj.B));
Console.WriteLine("A = {0}, Bs = [ {1} ] ", aKey, bList);
}
Here's the working demo: http://ideone.com/fXgQ3
You can use The GroupAdjacent Extension Method .
Then , you just need
var grps = objects.GroupAdjacent(p => new { p.A });
I think it is the easiest way to implement it .
EDIT:
Here is my test code.
class Program
{
static void Main(string[] args)
{
var ia = new Dummycls[] {
new Dummycls{ A = 1, B = 1 },
new Dummycls{ A = 1, B = 2 },
new Dummycls{ A = 2, B = 3 },
new Dummycls{ A = 2, B = 4 },
new Dummycls{ A = 1, B = 5 },
new Dummycls{ A = 3, B = 6 },
};
var groups = ia.GroupAdjacent(i => i.A);
foreach (var g in groups)
{
Console.WriteLine("Group {0}", g.Key);
foreach (var i in g)
Console.WriteLine(i.ToString());
Console.WriteLine();
}
Console.ReadKey();
}
}
class Dummycls
{
public int A { get; set; }
public int B { get; set; }
public override string ToString()
{
return string.Format("A={0};B={1}" , A , B);
}
}
The result is
Group 1
A=1;B=1
A=1;B=2
Group 2
A=2;B=3
A=2;B=4
Group 1
A=1;B=5
Group 3
A=3;B=6
This is the structure of a method that does what you want:
public static IEnumerable<IGrouping<TKey, TElement>> GroupWithKeyBreaks<T, TKey, TElement>(IEnumerable<T> enumerable,
Func<T, TKey> keySelector,
Func<T, TElement> itemSelector)
{
// Error handling goes here
TKey currentKey = default(TKey);
List<TElement> elements = new List<TElement>();
foreach (T element in enumerable)
{
TKey thisKey = keySelector(element);
if (thisKey == null)
{
continue;
}
if (!thisKey.Equals(currentKey) && elements.Count > 0)
{
yield return new SimpleGrouping<TKey, TElement>(currentKey, elements);
elements = new List<TElement>();
}
elements.Add(itemSelector(element));
currentKey = thisKey;
}
// Add the "last" item
if (elements.Count > 0)
{
yield return new SimpleGrouping<TKey, TElement>(currentKey, elements);
}
}
It uses the following helper class:
private class SimpleGrouping<T, U> : IGrouping<T, U>
{
private T key;
private IEnumerable<U> grouping;
T IGrouping<T, U>.Key
{
get { return key; }
}
IEnumerator<U> IEnumerable<U>.GetEnumerator()
{
return grouping.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return grouping.GetEnumerator();
}
public SimpleGrouping(T k, IEnumerable<U> g)
{
this.key = k;
this.grouping = g;
}
}
Here's a sample usage:
foreach (var grouping in data.GroupWithKeyBreaks(x => x.A, x => x.B))
{
Console.WriteLine("Key: " + grouping.Key);
foreach (var element in grouping)
{
Console.Write(element);
}
}
var groupCounter = 0;
int? prevA = null;
collection
.Select(item => {
var groupId = item.A == prevA ? groupCounter : ++groupCounter;
prevA = item.A;
return new { groupId, item.A, item.B };
})
.GroupBy(item => item.groupId)
.Select(grp => new { A = grp.First().A, Bs = grp.Select(g => g.B) });
If your collection is in o, then:
var trans = o.Aggregate
(
new {
List = new List<Tuple<int, List<int>>>(),
LastSeed = (int?)0
},
(acc, item) =>
{
if (acc.LastSeed == null || item.A != acc.LastSeed)
acc.List.Add(Tuple.Create(item.A, new List<int>()));
acc.List[acc.List.Count - 1].Item2.Add(item.B);
return new { List = acc.List, LastSeed = (int?)item.A};
},
acc => acc.List.Select(
z=>new {A = z.Item1,
B = z.Item2 as IEnumerable<int>
})
);
This produces an IEnumerable<int, IEnumerable<int>> of the required form.
var result = list.ToKeyValuePairs(x => x.A)
.Select(x => new { A = x.Key, Bs = x.Value.Select(y => y.B) });
foreach (var item in result)
{
Console.WriteLine("A = {0} Bs=[{1}]",item.A, String.Join(",",item.Bs));
}
-
public static class MyExtensions
{
public static IEnumerable<KeyValuePair<S,IEnumerable<T>>> ToKeyValuePairs<T,S>(
this IEnumerable<T> list,
Func<T,S> keySelector)
{
List<T> retList = new List<T>();
S prev = keySelector(list.FirstOrDefault());
foreach (T item in list)
{
if (keySelector(item).Equals(prev))
retList.Add(item);
else
{
yield return new KeyValuePair<S, IEnumerable<T>>(prev, retList);
prev = keySelector(item);
retList = new List<T>();
retList.Add(item);
}
}
if(retList.Count>0)
yield return new KeyValuePair<S, IEnumerable<T>>(prev, retList);
}
}
OUTPUT:
A = 1 Bs=[1,2]
A = 2 Bs=[3,4]
A = 1 Bs=[5]
A = 3 Bs=[6]