Flatten a collection based on an internal property - c#

Given the follow class structure:
int Id;
string[] Codes;
And the following data:
Foo { Id = 1, Codes = new[] { "01", "02" } }
Foo { Id = 2, Codes = new[] { "02", "03" } }
Foo { Id = 3, Codes = new[] { "04", "05" } }
I would like to end up with the following structure.
Code = "01", Id = 1
Code = "02", Id = 1
Code = "02", Id = 2
Code = "03", Id = 2
Code = "04", Id = 3
Code = "05", Id = 3
I've got the following query, but it's giving me a collection as the Id rather than the flat structure I am after.
collection.GroupBy(f => f.Codes.SelectMany(c => c), f => f.Id,
(code, id) => new { Code = code, Id = id })
.ToArray()
What am I missing?

SelectMany can return multiple elements for each item as a single list
items
.SelectMany(foo => foo.Codes.Select(code => new { Id = foo.Id, Code = code }));

The answer of Diego Torres is correct; I would only add to it that this query is particularly concise and readable in the comprehension form:
var q = from foo in foos
from code in foo.Codes
select new { Code = code, foo.Id };

Related

C# sort object list with start position and loop

I have a strange question :)
I have a object list looking like this:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
I would like to sort the list providing a "start id"
For example, if I provide "start id" 3, the result should look like this:
Id
Name
3
Patric
4
Theodor
1
Marcus
2
Mattias
I have no idea where to start, so I really need some help from you coding gods
The list is from a sql table, but it does not matter for me where the sort take place (in sql query or in c# code)
Try this:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
var start_id = 3;
var max_id = list.Max(y => y.Id);
var result =
from x in list
orderby (x.Id + max_id - start_id) % max_id
select x;
I get:
With LINQ to objects you can do something like that:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
var startId = 3;
var result = list
.GroupBy(i => i.Id >= startId ? 1 : 0) // split in two groups
.OrderByDescending(g => g.Key) // sort to have the group with startId first
.Select(g => g.OrderBy(i => i.Id)) // sort each group
.SelectMany(i => i) // combine result
.ToList();
Console.WriteLine(string.Join(", ", result.Select(i => i.Id))); // prints "3, 4, 1, 2"
You require 2 criteria to apply:
Order ascending by Id.
Return the Ids greater than threshold before the Ids less than threshold.
You can try:
var offset = 3;
var sorted1 = list
.OrderBy(item => item.Id < offset)
.ThenBy(item => item.Id);
The OrderBy condition yields true if Id is less than offset and false otherwise.
true is greater than false and therefore is returned later
A dirty way could also be:
var offset = 3;
var sorted2 = list
.OrderBy(item => unchecked((uint)(item.Id - offset)));
Here the offset is subtracted from Id and the result converted to unsigned int to make the negative values become very large positive ones. A little hacky. Might not work with queries against SQL providers.
Here's a toy Non-Linq Version
object[] ShiftList(int id)
{
var list = new dynamic[]
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
Span<dynamic> listSpan = list;
int indexFound = -1;
for (int i = 0; i < list.Length; i++)
{
if (listSpan[i].Id == id)
{
indexFound = i;
}
}
if (indexFound is -1)
{
return list;
}
var left = listSpan.Slice(0, indexFound);
var right = listSpan[indexFound..];
object[] objs = new object[list.Length];
Span<object> objSpan = objs;
right.CopyTo(objSpan);
left.CopyTo(objSpan[right.Length..]);
return objs;
}
Try using foreach and iterate over each object in your list:
foreach (var item in list)
{
}
from here you should be able to use some of the collection methods for a list to reorder your list.

c#: Move element whose ID is in array to top of list

In C#,I have List of Employee object. Employee class is
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
}
In List objected are sorted based on Employee.ID. I have an array of int which is basically Employee.ID which I want on top of the list and in list,order must remain same as in array.
If I hava input like this
List:
[
{ID:1,Name:A},
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
and Array: {2,3,1}
Then I want Output List:
[
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:1,Name:A},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
And I have done this
foreach (int i in a)
{
list = list.OrderBy(x => x.ID != i).ToList();
}
//a is array
//list is List
Any better Solution.Thanks in advance.
After you got your list sorted based on the ID just iterate the array and move the elements. In order to do this you need to first remove and then insert the item at the correct position.
for(int i = 0; i < myArray.Length; i++)
{
var e = myList.Single(x => x.Id == myArray[i]);
myList.Remove(e);
myList.Insert(i, e);
}
You may also want to use SingleOrDefault instead of Single to verify that myList even contains the element with the current id, e.g. when your array contains [2, 3, 101]
To add another version to the mix. The complete sorting can be done in one go:
list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? int.MaxValue : i; }).ToList();
where list is the EmployeeList and a the indices array. (NB, the for loop is not needed, the above should do both sortings).
Inside the OrderBy callback, if the id is not inside a, int.MaxValue is returned to place it after the ones inside the array (a.Length would work as well). OrderBy should maintain the original order of the enumeration (list) for those elements that return the same value.
PS, if you want to sort first by index inside a and the rest on the ids (not necessarily the original order), you can use the following (as long as a.Length + largest ID < int.MaxValue) : list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? a.Length + e.ID : i; }).ToList();
Here's a way to do it in pure LINQ, without changing the original sequence.
Broken into steps to see what's going on.
public static void Main()
{
var employeeList = new List<Employee>()
{
new Employee(){ ID= 1,Name= "A"},
new Employee() { ID= 2,Name= "B"},
new Employee() { ID= 3,Name= "AA"},
new Employee() { ID= 4,Name= "C"},
new Employee() { ID= 5,Name= "CD"},
new Employee() { ID= 100,Name= "Z"}
};
var orderByArray = new int[] { 2, 3, 1, 100, 5, 4 };
var sortPos = orderByArray.Select((i, index) => new { ID = i, SortPos = index });
var joinedList = employeeList.Join(sortPos, e => e.ID, sp => sp.ID, (e, sp) => new { ID = e.ID, Name = e.Name, SortPos = sp.SortPos });
var sortedEmployees = joinedList.OrderBy(e => e.SortPos).Select(e => new Employee { ID = e.ID, Name = e.Name });
}
Try this using LINQ:
List<Employee> employees = ...
int[] ids = ...
var orderEmployees = ids.Select(id => employees.Single(employee => employee.ID == id))
.Concat(employees.Where(employee => !ids.Contains(employee.ID)).ToList();
Foreach id in ids array we will grab the matching employee and we will concat to it all the employees that their id does not exist in ids array.
I like to use a special Comparer for that, it seems clearer to me, though a bit more code. It hides the complexity of the sort in the comparer class, and then you can just call it with :
theList.OrderBy(x => x.id, new ListOrderBasedComparer(sortList));
It will sort according to any list passed to the comparer when instantiating, and will put elements not in the "known sort list" at the end.
You can of course adapt it to your special needs.
public class ListOrderBasedComparer: Comparer<int>
{
private List<int> sortList;
public ListOrderBasedComparer(List<int> sortList)
{
// if you want you can make constructor accept arrays and convert it
// (if you find that more convenient)
this.sortList = sortList;
}
public override int Compare(int x, int y)
{
var indexOfX = sortList.FindIndex(a => a == x);
var indexOfY = sortList.FindIndex(a => a == y);
// handle elements not in sortArray : if not in sort array always assume they should be "less than the others" and "equal between them".
if (indexOfX == -1 && indexOfY == -1) return 0;
if (indexOfY == -1) return -1;
if (indexOfX == -1) return 1;
// if elements are in sortArray (FindIndex returned other than -1), use usual comparison of index values
return indexOfX.CompareTo(indexOfY);
}
}
Example on how to use it, with Linq :
public class TestCompare
{
public void test ()
{
var myArray = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var myArray2 = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 0, name = "X" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 23, name = "Z"},
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var sortList = new List<int> { 2, 3, 1, 4, 5, 6 };
// good order
var mySortedArray = myArray.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
// good order with elem id 0 and 23 at the end
var mySortedArray2 = myArray2.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
}
}
public class MyClass
{
public int id;
public string name;
}

Linq reducing one-to-many relationship to one-to-one

List<dynamic> a = new List<dynamic>();
a.Add(new { Foo = 1, Baz = "Inga", Name = "Alice"});
a.Add(new { Foo = 2, Baz = "Baz", Name = "Bob"});
a.Add(new { Foo = 3, Baz = "Hi", Name = "Charlie"});
List<dynamic> b = new List<dynamic>();
b.Add(new { Foo = 1, Value = "Bar", Code = "A"});
b.Add(new { Foo = 1, Value = "Quux", Code = "B"});
b.Add(new { Foo = 2, Value = "Bar", Code = "C"});
b.Add(new { Foo = 3, Value = "Mint", Code = "A"});
b.Add(new { Foo = 3, Value = "Seven", Code = "Q"});
b.Add(new { Foo = 3, Value = "Threeve", Code = "T"});
Ok....so I have a problem(naturally)
This is contrived and simplified to focus on the problem at hand.
I need to modify a Linq query to project the two Lists to the following response:
[
{ Foo = 1
, Baz = "Inga"
, Code = "A"
, Bars = [{ Value = "Bar", Code = "A"}
,{ Value = "Quux", Code = "B"}
]
}
,{ Foo = 2
, Baz = "Baz"
, Code = "C"
, Bars = [{ Value = "Fizz", Code = "C"}]
}
,{ Foo = 3
, Baz = "Hi"
, Code = "A"
, Bars = [{ Value = "Mint", Code = "A"}
,{ Value = "Seven", Code = "Q"}
,{ Value = "Threeve", Code = "T"}
]
}
]
First, the TL;DR
Is there any way to query collection b to select
(b.First Where Distinct By b.Foo) AsEnumerable() ?
...The long version
I need to select a projection of a but as it is being materialized, identify the first Code in list b where b.Foo == a.Foo and put b.Code directly on a. Then the items from b where b.Foo == a.Foo need to be put into a.Bars.
The problem I have on my hands is that I am not identifying a singular a so I can't preselect the a and b values to simplify this mess and there's no opportunity to remodel.
So, if I want to search Where Value = Bar; Alice and Bob need to be returned with the proper mapping and projection.
The naive attempt would be...
var results = a.Join( b
, master => master.Foo
, detail => detail.Foo
, (master, detail) => new { master, detail})
.Select(item => new
{
item.master.Foo
, item.master.Baz
, item.master.Name
, item.detail.Code
, Bars = b.Select(x => x.Foo.Equals(item.master.Foo))
};
but this causes my results to contain duplicate "Alice" records and duplicate "Charlie" records because it inner joined a and b. What I really want to do (pseudo) is
a.Join(
b.Where(b.Foo.Equals(a.Foo)).First()
, master => master.Foo
, detail => detail.Foo
, (master, detail) => new { master, detail}
)
.Select(item => new
{
item.master.Foo
, item.master.Baz
, item.master.Name
, item.detail.Code
, Bars = b.Select(x => x.Foo.Equals(item.master.Foo))
};
but no matter what I try, it comes out a mess.
...Note, I can't take the naive approach and then run a DistinctBy because the projection is anonymous.
Can anyone resolve this purely with Linq to Object queries? (Note: I'm not needing a single pass resolution)
I would just stick with grouping the second group (denoted b here) and then using that paired with a find to compose the projection.
var results = b.GroupBy( d => d.Foo ).Select( g => new {
Foo = g.Key,
Baz = a.First( i => i.Foo == g.Key ).Baz,
Code = g.First().Code,
Bars = g.Select( e => new { Value = e.Value, Code = e.Code }).ToArray()
});
If I understand correctly, the Code in a single result entry is just the Code of the first joined b element.
So try this:
var result = a.GroupJoin(b,
a0 => a0.Foo,
b0 => b0.Foo,
(a0, bs) =>
new
{
Foo = a0.Foo,
Baz = a0.Baz,
Code = bs.Select(b1 => b1.Code).FirstOrDefault(),
Bars = bs.Select(b1 => new {b1.Value, b1.Code}).ToArray()
}).ToArray();
GroupJoin is what you need here. You can think of Join like SelectMany while GroupJoin - like Select. The difference is the type of the second argument of the projection - TInner for Join and IEnumerable<TInner> for GroupJoin. In LINQ syntax the GroupJoin is achieved by into clause.
With all that being said, here is how it looks for your example in both syntaxes:
var resultsA = a.GroupJoin(b, master => master.Foo, detail => detail.Foo, (master, details) => new
{
master.Foo,
master.Baz,
master.Name,
Code = details.Select(detail => detail.Code).First(),
Bars = details.Select(detail => new { detail.Value, detail.Code })
});
var resultsB =
from master in a
join detail in b on master.Foo equals detail.Foo into details
select new
{
master.Foo,
master.Baz,
master.Name,
Code = details.Select(detail => detail.Code).First(),
Bars = details.Select(detail => new { detail.Value, detail.Code })
};
var query = from ai in a
let bs = b.Where(bi => bi.Foo == ai.Foo)
select new
{
ai.Foo,
ai.Baz,
Code = bs.Select(bi => bi.Code).FirstOrDefault(),
Bars = bs.Select(bi => new { bi.Value, bi.Code }),
};

Linq query to group by field1, count field2 and filter by count between values of joined collection

I'm having trouble with getting a my linq query correct. I've been resisting doing this with foreach loops because I'm trying to better understand linq.
I have following data in LinqPad.
void Main()
{
var events = new[] {
new {ID = 1, EventLevel = 1, PatientID = "1", CodeID = "2", Occurences = 0 },
new {ID = 2, EventLevel = 2, PatientID = "1", CodeID = "2", Occurences = 0 },
new {ID = 3, EventLevel = 1, PatientID = "2", CodeID = "1", Occurences = 0 },
new {ID = 4, EventLevel = 3, PatientID = "2", CodeID = "2", Occurences = 0 },
new {ID = 5, EventLevel = 1, PatientID = "3", CodeID = "3", Occurences = 0 },
new {ID = 6, EventLevel = 3, PatientID = "1", CodeID = "4", Occurences = 0 }
};
var filter = new FilterCriterion();
var searches = new List<FilterCriterion.Occurence>();
searches.Add(new FilterCriterion.Occurence() { CodeID = "1", MinOccurences = 2, MaxOccurences = 3 });
searches.Add(new FilterCriterion.Occurence() { CodeID = "2", MinOccurences = 2, MaxOccurences = 3 });
filter.Searches = searches;
var summary = from e in events
let de = new
{
PatientID = e.PatientID,
CodeID = e.CodeID
}
group e by de into t
select new
{
PatientID = t.Key.PatientID,
CodeID = t.Key.CodeID,
Occurences = t.Count(d => t.Key.CodeID == d.CodeID)
};
var allCodes = filter.Searches.Select(i => i.CodeID);
summary = summary.Where(e => allCodes.Contains(e.CodeID));
// How do I find the original ID property from the "events" collection and how do I
// eliminate the instances where the Occurences is not between MinOccurences and MaxOccurences.
foreach (var item in summary)
Console.WriteLine(item);
}
public class FilterCriterion
{
public IEnumerable<Occurence> Searches { get; set; }
public class Occurence
{
public string CodeID { get; set; }
public int? MinOccurences { get; set; }
public int? MaxOccurences { get; set; }
}
}
The problem I have is that need to filter the results by the MinOccurences and MaxOccurences filter property and in the end I want the "events" objects where the IDs are 1,2,3 and 4.
Thanks in advance if you can provide help.
To access event.ID at the end of processing you need to pass it with your first query. Alter select to this:
// ...
group e by de into t
select new
{
PatientID = t.Key.PatientID,
CodeID = t.Key.CodeID,
Occurences = t.Count(d => t.Key.CodeID == d.CodeID),
// taking original items with us
Items = t
};
Having done that, your final query (including occurrences filter) might look like this:
var result = summary
// get all necessary data, including filter that matched given item
.Select(Item => new
{
Item,
Filter = searches.FirstOrDefault(f => f.CodeID == Item.CodeID)
})
// get rid of those without matching filter
.Where(i => i.Filter != null)
// this is your occurrences filtering
.Where(i => i.Item.Occurences >= i.Filter.MinOccurences
&& i.Item.Occurences <= i.Filter.MaxOccurences)
// and finally extract original events IDs
.SelectMany(i => i.Item.Items)
.Select(i => i.ID);
This produces 1, 2 as result. 3 and 4 are left out as they don't get past occurrences filtering.
I have run your program in linqpad.
My understanding is that you want to filter using filter.MinOccurences and filter.MaxOccurences on Occurences count of result data set.
You can add additional filters using Where clause.
if (filter.MinOccurences.HasValue)
summary = summary.Where (x=> x.Occurences >= filter.MinOccurences);
if (filter.MaxOccurences.HasValue)
summary = summary.Where (x=> x.Occurences <= filter.MaxOccurences);

LINQ-to-objects index within a group + for different groupings (aka ROW_NUMBER with PARTITION BY equivalent)

After much Google searching and code experimentation, I'm stumped on a complex C# LINQ-to-objects problem which in SQL would be easy to solve with a pair of ROW_NUMBER()...PARTITION BY functions and a subquery or two.
Here's, in words, what I'm trying to do in code-- the underlying requirement is removing duplicate documents from a list:
First, group a list by (Document.Title, Document.SourceId), assuming a (simplified) class definition like this:
class Document
{
string Title;
int SourceId; // sources are prioritized (ID=1 better than ID=2)
}
Within that group, assign each document an index (e.g. Index 0 == 1st document with this title from this source, Index 1 = 2nd document with this title from this source, etc.). I'd love the equivalent of ROW_NUMBER() in SQL!
Now group by (Document.Title, Index), where Index was computed in Step #2. For each group, return only one document: the one with the lowest Document.SourceId.
Step #1 is easy (e.g. codepronet.blogspot.com/2009/01/group-by-in-linq.html), but I'm getting stumped on steps #2 and #3. I can't seem to build a red-squiggle-free C# LINQ query to solve all three steps.
Anders Heilsberg's post on this thread is I think the answer to Steps #2 and #3 above if I could get the syntax right.
I'd prefer to avoid using an external local variable to do the Index computation, as recommended on slodge.blogspot.com/2009/01/adding-row-number-using-linq-to-objects.html, since that solution breaks if the external variable is modified.
Optimally, the group-by-Title step could be done first, so the "inner" groupings (first by Source to compute the index, then by Index to filter out duplicates) can operate on small numbers of objects in each "by title" group, since the # of documents in each by-title group is usually under 100. I really don't want an N2 solution!
I could certainly solve this with nested foreach loops, but it seems like the kind of problem which should be simple with LINQ.
Any ideas?
I think jpbochi missed that you want your groupings to be by pairs of values (Title+SourceId then Title+Index). Here's a LINQ query (mostly) solution:
var selectedFew =
from doc in docs
group doc by new { doc.Title, doc.SourceId } into g
from docIndex in g.Select((d, i) => new { Doc = d, Index = i })
group docIndex by new { docIndex.Doc.Title, docIndex.Index } into g
select g.Aggregate((a,b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b);
First we group by Title+SourceId (I use an anonymous type because the compiler builds a good hashcode for the grouping lookup). Then we use Select to attach the grouped index to the document, which we use in our second grouping. Finally, for each group we pick the lowest SourceId.
Given this input:
var docs = new[] {
new { Title = "ABC", SourceId = 0 },
new { Title = "ABC", SourceId = 4 },
new { Title = "ABC", SourceId = 2 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 5 },
new { Title = "123", SourceId = 5 },
};
I get this output:
{ Doc = { Title = ABC, SourceId = 0 }, Index = 0 }
{ Doc = { Title = 123, SourceId = 5 }, Index = 0 }
{ Doc = { Title = 123, SourceId = 5 }, Index = 1 }
{ Doc = { Title = 123, SourceId = 7 }, Index = 2 }
Update: I just saw your question about grouping by Title first. You can do this using a subquery on your Title groups:
var selectedFew =
from doc in docs
group doc by doc.Title into titleGroup
from docWithIndex in
(
from doc in titleGroup
group doc by doc.SourceId into idGroup
from docIndex in idGroup.Select((d, i) => new { Doc = d, Index = i })
group docIndex by docIndex.Index into indexGroup
select indexGroup.Aggregate((a,b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b)
)
select docWithIndex;
To be honest, I'm quite confused with your question. Maybe if you should explain what you're trying to solve. Anyway, I'll try to answer what I understood.
1) First, I'll assume that you already have a list of documents grouped by Title+SourceId. For testing purposes, I hardcoded a list as follow:
var docs = new [] {
new { Title = "ABC", SourceId = 0 },
new { Title = "ABC", SourceId = 4 },
new { Title = "ABC", SourceId = 2 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 5 },
};
2) To get put a index in every item, you can use the Select extension method, passing a Func selector function. Like this:
var docsWithIndex
= docs
.Select( (d, i) => new { Doc = d, Index = i } );
3) From what I understood, the next step would be to group the last result by Title. Here's how to do it:
var docsGroupedByTitle
= docsWithIndex
.GroupBy( a => a.Doc.Title );
The GroupBy function (used above) returns an IEnumerable<IGrouping<string,DocumentWithIndex>>. Since a group is enumerable too, we now have an enumerable of enumerables.
4) Now, for each of the groups above, we'll get only the item with the minimum SourceId. To make this operation we'll need 2 levels of recursion. In LINQ, the outer level is a selection (for each group, get one of its items), and the inner level is an aggregation (get the item with the lowest SourceId):
var selectedFew
= docsGroupedByTitle
.Select(
g => g.Aggregate(
(a, b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b
)
);
Just to ensure that it works, I tested it with a simple foreach:
foreach (var a in selectedFew) Console.WriteLine(a);
//The result will be:
//{ Doc = { Title = ABC, SourceId = 0 }, Index = 0 }
//{ Doc = { Title = 123, SourceId = 5 }, Index = 4 }
I'm not sure that's what you wanted. If not, please comment the answer and I can fix the answer. I hope this helps.
Obs.: All the classes used in my tests were anonymous. So, you don't really need to define a DocumentWithIndex type. Actually, I haven't even declared a Document class.
Method Based Syntax:
var selectedFew = docs.GroupBy(doc => new {doc.Title, doc.SourceId}, doc => doc)
.SelectMany((grouping) => grouping.Select((doc, index) => new {doc, index}))
.GroupBy(anon => new {anon.doc.Title, anon.index})
.Select(grouping => grouping.Aggregate((a, b) => a.doc.SourceId <= b.doc.SourceId ? a : b));
Would you say the above is the equivalent Method based syntax?
I implemented an extension method. It supports multiple partition by fields as well as multiple order conditions.
public static IEnumerable<TResult> Partition<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<IEnumerable<TSource>, IOrderedEnumerable<TSource>> sorter,
Func<TSource, int, TResult> selector)
{
AssertUtilities.ArgumentNotNull(source, "source");
return source
.GroupBy(keySelector)
.Select(arg => sorter(arg).Select(selector))
.SelectMany(arg => arg);
}
Usage:
var documents = new[]
{
new { Title = "Title1", SourceId = 1 },
new { Title = "Title1", SourceId = 2 },
new { Title = "Title2", SourceId = 15 },
new { Title = "Title2", SourceId = 14 },
new { Title = "Title3", SourceId = 100 }
};
var result = documents
.Partition(
arg => arg.Title, // partition by
arg => arg.OrderBy(x => x.SourceId), // order by
(arg, rowNumber) => new { RowNumber = rowNumber, Document = arg }) // select
.Where(arg => arg.RowNumber == 0)
.Select(arg => arg.Document)
.ToList();
Result:
{ Title = "Title1", SourceId = 1 },
{ Title = "Title2", SourceId = 14 },
{ Title = "Title3", SourceId = 100 }

Categories

Resources