How to group according to a field value - linq

I got result after executing linq query in mvc like below:
[0] = { albumid = 176, selecttionid = 243, orderid = 57 }
[1] = { albumid = 177, selecttionid = 243, orderid = 57 }
[2] = { albumid = 178, selecttionid = 243, orderid = 57 }
[3] = { albumid = 19, selecttionid = 321, orderid = 137 }
......
But I need to create folder for each different selecttionid .How can I do this?

If you just need to create a folder for each diferente selecttionid, than you just need use Select with Distinct, like that:
var selections = mylist.Select(x => x.selecttionid).Distinct();
foreach(var selection in selections)
{
//Code that create a folder for the selectionId
}
If you need the values from the list, than you can use GroupBy.
var groupedSelections = mylist.GroupBy(x => x.selecttionid);
foreach(var groupSelecion in groupedSelections)
{
//Code that create a folder for the groupSelecion.Key
}

Related

Filter Table data using Linq

I want filter the messages with type X from table below whose TO value is not equal to FROM value of any Type Y records in same table. (eg. ee) using Linq.
Message TO FROM Type
------- ---- ----- ------
aa 11 22 X
bb 33 44 X
cc 55 11 Y
dd 66 33 Y
ee 77 88 X
I have used this but not working
var messages1 = messages.Where(x => x.Type == 'X');
var messages2 = messages.Where(x => x.Type == 'Y');
var filteredMessages = messages1
.Where(x => !messages2.Any(y => y.From == x.To));
I am not able to reproduce your issue, although I am getting the correct output.
private static void Main(string[] args)
{
List<MessageItem> messages = new List<MessageItem>
{
new MessageItem
{
Message = "aa",
From = 22,
To = 11,
Type = "X"
},
new MessageItem
{
Message = "bb",
From = 44,
To = 33,
Type = "X"
},
new MessageItem
{
Message = "cc",
From = 11,
To = 55,
Type = "Y"
},
new MessageItem
{
Message = "dd",
From = 33,
To = 66,
Type = "Y"
},
new MessageItem
{
Message = "ee",
From = 88,
To = 77,
Type = "X"
}
};
var messages1 = messages.Where(x => x.Type == "X").ToList();
var messages2 = messages.Where(x => x.Type == "Y").ToList();
var filteredMessages = messages1.FindAll(x => !messages2.Any(y => x.To == y.From));
}
The model class:
public class MessageItem
{
public string Message { get; set; }
public int From { get; set; }
public int To { get; set; }
public string Type { get; set; }
}
Outputting:

Linq. Query Sum on dataset

I'm a beginner in linq.
I would like to write this query in linq.
Select SUM(importo), anno From(
Select anno, Importo from[Archivio].[dbo].[AAA]
union
Select anno, importo From[Archivio].[dbo].[BBB]) as prova
group by Anno order by anno.
I wrote this:
DataTable Tab_AAA = DS_AAA.Tables[0];
DataTable Tab_BBB = DS_BBB.Tables[0];
IEnumerable<DataRow> query =
(from A in DS_AAA.AsEnumerable()
select A).Union(from B in DS_BBB.AsEnumerable()
select B)
now I can not enter the sum and the group by.
Thank you all
the objects that you are using must be something like this
public class AnnoImporto
{
private int _anno;
private double _importo;
private string _altro;
public AnnoImporto(int aAnno, double aImp, string aAltro)
{
_anno = aAnno;
_importo = aImp;
_altro = aAltro;
}
public int Anno
{
get { return _anno; }
set { _anno = value; }
}
public double Importo
{
get { return _importo; }
set { _importo = value; }
}
public string Altro
{
get { return _altro; }
set { _altro = value; }
}
}
surely you have the year and the amount, think to "altro" as generic eventual other fields. I built two lists of AnnoImporto objects, but the following code will work even on lists of different object (of course, as I just said, just having year and amount...)
List<AnnoImporto> aaa = new List<AnnoImporto>();
List<AnnoImporto> bbb = new List<AnnoImporto>();
aaa.Add(new AnnoImporto(2015, 10,"blabla"));
aaa.Add(new AnnoImporto(2014, 20, "blabla"));
aaa.Add(new AnnoImporto(2013, 15, "blabla"));
aaa.Add(new AnnoImporto(2012, 5, "blabla"));
aaa.Add(new AnnoImporto(2011, 40, "blabla"));
bbb.Add(new AnnoImporto(2011, 50, "blabla"));
bbb.Add(new AnnoImporto(2013, 20, "blabla"));
bbb.Add(new AnnoImporto(2015, 15, "blabla"));
var aaa_mod = from a in aaa
select new
{
Anno = a.Anno,
Importo = a.Importo
};
var bbb_mod = from b in bbb
select new
{
Anno = b.Anno,
Importo = b.Importo
};
var unione = aaa_mod.Union(bbb_mod);
Console.WriteLine("controlliamo la nuova lista:");
foreach (var p in unione)
{
Console.WriteLine($"{p.Anno}, {p.Importo}");
}
var somme = from p in unione
orderby p.Anno
group p.Importo by p.Anno into q
select new
{
Anno = q.Key,
ImportoTotale = q.Sum()
};
Console.WriteLine("controlliamo le somme:");
foreach (var p in somme)
{
Console.WriteLine($"{p.Anno}, {p.ImportoTotale}");
}
I followed step by step your query: first I selected in aaa and in bbb only the fields you need, I made the union of the results and I checked it. Then I did the group by to calculate sums by year.

minOccurs attribute in #Group annotation causes UnexpectedRecordException

I am new to Bean-IO and I was trying to configure a validation logic for occurrence of group when a particular record type is available in the file. For example if there are three records in flat file as shown below.
560866
670972
57086659
I am trying to setup the following logic
Both 56 and 67 lines together form a multi line record
56&67 records can come independently of record 57,but 57 record cannot come without 56&67.
I was successful in creating the first validation using minOccurs attribute in #record annotation, but was not able to do the same for 56&67 using a group.
Please find the sample code setup below.
HeaderRecord class holds the 56&67 record details
#Group
public class HeaderRecord {
#Record(minOccurs = 1)
public TX56 tx56;
#Record(minOccurs = 1)
public TX67 tx67;
}
RecordObject is used to hold the headers and line items
public class RecordObject {
#Group(collection = List.class, minOccurs = 1)
List<HeaderRecord> headerRecords;
#Record(collection = List.class)
List<TX57> tx57s;
}
#Record(maxLength = 10, name = "TX56")
public class TX56 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "56", trim = true)
protected int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true)
protected int number;
}
#Record(maxLength = 31, name = "TX67")
public class TX67 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "67", trim = true)
protected int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true)
protected int number;
}
#Record(maxLength = 71, name = "TX57")
public class TX57 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "57", trim = true)
protected int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true)
protected int number;
}
with the above configuration when I try to parse the file with records given below, it throws UnexpectedRecordException.
560866
670972
57086659
Stack trace:
2018-07-17 15:22:07,778[http-nio-8080-exec-2]ERROR
org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet]-Servlet.service()for
servlet[dispatcherServlet]in context with path[]threw
exception[Request processing failed;nested exception is
org.beanio.UnexpectedRecordException:End of stream reached,expected
record'tx56']with root cause org.beanio.UnexpectedRecordException:End
of stream reached,expected record'tx56' at
org.beanio.internal.parser.UnmarshallingContext.newUnsatisfiedRecordException(UnmarshallingContext.java:367)~[beanio-2.1.0.jar:2.1.0]
at
org.beanio.internal.parser.Group.unmarshal(Group.java:127)~[beanio-.1.0.jar:2.1.0]
at
org.beanio.internal.parser.DelegatingParser.unmarshal(DelegatingParser.java:39)~[beanio-2.1.0.jar:2.1.0]
at
org.beanio.internal.parser.RecordCollection.unmarshal(RecordCollection.java:42)~[beanio-2.1.0.jar:2.1.0]
at
org.beanio.internal.parser.Group.unmarshal(Group.java:118)~[beanio-2.1.0.jar:2.1.0]
at
org.beanio.internal.parser.BeanReaderImpl.internalRead(BeanReaderImpl.java:106)~[beanio-2.1.0.jar:2.1.0]
at
org.beanio.internal.parser.BeanReaderImpl.read(BeanReaderImpl.java:67)~[beanio-2.1.0.jar:2.1.0]
at
dk.coop.integration.fileconversion.service.sampleapplication.createFixedLengthFile(sampleapplication.java:32)~[classes/:?]
Note:
with the above configuration, following scenarios works
56&67comes independently
560866
670972
57cannot come independently
57086659:this flat file fails with a proper exception
56&67should always come as a single record.
this also works fine.
Additional Details:
Sample Flatfile
560866
670972
560866
670972
560866
670972
57086659
57086659
57086659
57086659
52022
560866
670972
57086659
As seen above, in the flat file there is a possibility that multiple header records and TX57 record can come as a single entity. Also there can be other type of records that can come in between, in which case I have to treat second occurrence of TX56,67 and 57 as a different item.
In the above example first 10 records will form a single recordObject, then the second occurrence of these records will form a second record object. Sorry for not sharing earlier, but there is another wrapper class which holds a list of recordObject.
I am giving the working maven project Github URL below. https://github.com/Jayadeep2308/FlatFileParser
EDIT: Updated after all requirements on 5 Aug 2018
I have made all the fields private inside the classes and assume that you have the getters + setters in place.
I have tried various combinations of settings on the #Group and #Record annotations so the code below might not be optimal, but should work.
First the main group (WrapperObject) that holds all your data:
#Group(minOccurs = 1, maxOccurs = 1)
public class WrapperObject {
#Group(minOccurs = 0, maxOccurs = 1, collection = List.class)
private List<RecordObject> recordObjectList;
#Record(minOccurs = 0, maxOccurs = -1, collection = List.class)
private List<TX52> tx52s;
}
EDIT: RecordObject updated to hold a list of HeaderRecord, also changed the #Group values.
#Group(minOccurs = 0, maxOccurs = -1)
public class RecordObject {
#Group(minOccurs = 0, maxOccurs = -1, collection = List.class)
private List<HeaderRecord> headerRecords;
#Record(minOccurs = 0, maxOccurs = -1, collection = List.class)
private List<TX57> tx57s;
}
#Group(minOccurs = 0, maxOccurs = -1)
public class HeaderRecord {
#Record(minOccurs = 1, maxOccurs = 1)
private TX56 tx56;
#Record(minOccurs = 1, maxOccurs = 1)
private TX67 tx67;
}
On the individual TX records I have added the required=true attribute on the #Field annotation for your record identifier fields.
Edit: Added TX52
#Record(maxLength = 74, name = "TX52")
public class TX52 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "52", trim = true, required = true)
private int id;
#Field(ordinal = 1, at = 2, length = 3, trim = true)
private int number;
}
#Record(maxLength = 10, name = "TX56")
public class TX56 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "56", trim = true, required = true)
private int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true, required = true)
private int number;
}
#Record(maxLength = 31, name = "TX67")
public class TX67 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "67", trim = true, required = true)
private int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true)
private int number;
}
#Record(maxLength = 71, name = "TX57")
public class TX57 {
#Field(ordinal = 0, at = 0, length = 2, rid = true, literal = "57", trim = true, required = true)
private int id;
#Field(ordinal = 1, at = 2, length = 4, trim = true)
private int number;
}
Lastly, my test code: (EDIT: Updated test data)
#Test
public void test() {
final StreamFactory factory = StreamFactory.newInstance();
final StreamBuilder builder = new StreamBuilder("Jaydeep23")
.format("fixedlength")
.parser(new FixedLengthParserBuilder())
.addGroup(WrapperObject.class);
factory.define(builder);
final String scenario1 = "560866\n670972\n560866\n670972\n560866\n670972";
final String scenario2 = "560866\n670972\n560866\n670972\n560866\n670972\n57086659\n57086659\n57086659\n" +
"57086659\n560866\n670972\n57086659\n560866\n670972";
// invalid
final String scenario3 = "57086659\n57086659\n57086659\n57086659\n57086659";
final String scenario4 = "52022\n52066\n52054\n52120";
final String scenario5 = scenario1;
final String scenario6 = "560866\n670972\n560866\n670972\n560866\n670972\n57086659\n57086659\n57086659\n" +
"57086659\n52021\n52022\n52023\n560866\n670972\n57086659\n52023";
final String message = scenario1;
BeanReader beanReader = null;
Object object = null;
try (final Reader in = new BufferedReader(new StringReader(message))) {
beanReader = factory.createReader("Jaydeep23", in);
beanReader.setErrorHandler(new LoggingBeanReaderErrorHandler());
while ((object = beanReader.read()) != null) {
System.out.println("Object = " + object);
}
} catch (final Exception e) {
fail(e.getMessage());
} finally {
if (beanReader != null) {
beanReader.close();
}
}
}
Generates this output: (EDIT: using your toString() methods)
Scenario 1 = [[Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
]null]null
Scenario 2 = [[Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
][Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
]]null
Scenario 3 - gives this error (which is correct according as TX57 is not allowed on its own:
Expected record/group 'tx56' at line 6
Scenario 4 = null[Record Type = 52, Store Number = 22
, Record Type = 52, Store Number = 66
, Record Type = 52, Store Number = 54
, Record Type = 52, Store Number = 120
]
Scenario 5 = [[Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
]null]null
Scenario 6 = [[Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
, Record Type = 56, Store Number = 866
Record Type = 67, Store Number = 972
][Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
, Record Type = 57, Store Number = 866
]][Record Type = 52, Store Number = 21
, Record Type = 52, Store Number = 22
, Record Type = 52, Store Number = 23
]
Hope this helps.
Let me know if it is now working for you.

PIVOT with LINQ from Datatable [duplicate]

I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}

Simple LINQ query

I have a List of X items. I want to have LINQ query that will convert it into batches (a List of Lists), where each batch has 4 items, except for the last one which can have 1-4 (whatever the remainder is). Also, the number 4 should be configurable so it could 5, 17, etc.
Can anyone tell me how to write that?
List<Item> myItems = ...;
List<List<Item>> myBatches = myItems.????
Thank you in advance!
If you're happy with the results being typed as IEnumerable<IEnumerable<T>> then you can do this:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val);
If you want an actual List<List<T>> then you'll need to add a couple of extra ToList calls:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val,
(k, g) => g.ToList())
.ToList();
Here is a good article about using Take and Skip to do paging, which is identical functionality to what you are requesting. It doesn't get you all of the way to a single line of LINQ, but hopefully helps.
This made me think of how we did this before LINQ.
var vessels = new List<Vessel>()
{ new Vessel() { id = 8, name = "Millennium Falcon" },
new Vessel() { id = 4, name = "Ebon Hawk" },
new Vessel() { id = 34, name = "Virago"},
new Vessel() { id = 12, name = "Naboo royal starship"},
new Vessel() { id = 17, name = "Radiant VII"},
new Vessel() { id = 7, name = "Lambda-class shuttle"},
new Vessel() { id = 23, name = "Rogue Shadow"}};
var chunksize=2;
// With LINQ
var vesselGroups = vessels.Select((v, i) => new { Vessel = v, Index = i })
.GroupBy(c => c.Index / chunksize, c => c.Vessel, (t,e)=>e.ToList())
.ToList();
// Before LINQ (most probably not optimal)
var groupedVessels = new List<List<Vessel>>();
var g = new List<Vessel>();
var chunk = chunksize;
foreach(var vessel in vessels)
{
g.Add(vessel);
chunk--;
if (chunk == 0)
{
groupedVessels.Add(g);
g = new List<Vessel>();
chunk = chunksize;
}
}
groupedVessels.Add(g);

Resources