У меня есть файл CSV с записями, которые необходимо отсортировать, а затем сгруппировать в пакеты произвольного размера (например, 300 максимальных записей в пакете). В каждом пакете может быть менее 300 записей, поскольку содержимое каждого пакета должно быть однородным (на основе содержимого нескольких разных столбцов).
Мое заявление LINQ, вдохновленное этим ответом на пакетная обработка с помощью LINQ выглядит следующим образом:
var query = (from line in EbrRecords
let EbrData = line.Split('\t')
let Location = EbrData[7]
let RepName = EbrData[4]
let AccountID = EbrData[0]
orderby Location, RepName, AccountID).
Select((data, index) => new {
Record = new EbrRecord(
AccountID = EbrData[0],
AccountName = EbrData[1],
MBSegment = EbrData[2],
RepName = EbrData[4],
Location = EbrData[7],
TsrLocation = EbrData[8]
)
,
Index = index}
).GroupBy(x => new {x.Record.Location, x.Record.RepName, batch = x.Index / 100});
«/ 100» дает мне произвольный размер ведра. Другие элементы groupby предназначены для достижения однородности между партиями. Я подозреваю, что это почти то, что я хочу, но это дает мне следующую ошибку компилятора: A query body must end with a select clause or a group clause
. Я понимаю, почему я получаю сообщение об ошибке, но в целом я не знаю, как исправить этот запрос. Как это сделать?
ОБНОВЛЕНИЕ Я почти добился того, что мне нужно, со следующим:
List<EbrRecord> input = new List<EbrRecord> {
new EbrRecord {Name = "Brent",Age = 20,ID = "A"},
new EbrRecord {Name = "Amy",Age = 20,ID = "B"},
new EbrRecord {Name = "Gabe",Age = 23,ID = "B"},
new EbrRecord {Name = "Noah",Age = 27,ID = "B"},
new EbrRecord {Name = "Alex",Age = 27,ID = "B"},
new EbrRecord {Name = "Stormi",Age = 27,ID = "B"},
new EbrRecord {Name = "Roger",Age = 27,ID = "B"},
new EbrRecord {Name = "Jen",Age = 27,ID = "B"},
new EbrRecord {Name = "Adrian",Age = 28,ID = "B"},
new EbrRecord {Name = "Cory",Age = 29,ID = "C"},
new EbrRecord {Name = "Bob",Age = 29,ID = "C"},
new EbrRecord {Name = "George",Age = 29,ID = "C"},
};
//look how tiny this query is, and it is very nearly the result I want!!!
int i = 0;
var result = from q in input
orderby q.Age, q.ID
group q by new { q.ID, batch = i++ / 3 };
foreach (var agroup in result)
{
Debug.WriteLine("ID:" + agroup.Key);
foreach (var record in agroup)
{
Debug.WriteLine(" Name:" + record.Name);
}
}
Хитрость здесь заключается в том, чтобы обойти перекрытие select «index position», используя переменную закрытия (в данном случае int i
). Результаты вывода следующие:
ID:{ ID = A, batch = 0 }
Name:Brent
ID:{ ID = B, batch = 0 }
Name:Amy
Name:Gabe
ID:{ ID = B, batch = 1 }
Name:Noah
Name:Alex
Name:Stormi
ID:{ ID = B, batch = 2 }
Name:Roger
Name:Jen
Name:Adrian
ID:{ ID = C, batch = 3 }
Name:Cory
Name:Bob
Name:George
Хотя этот ответ приемлем, он лишь немного меньше идеального результата. Должно быть, чтобы в первом появлении «партии 'B'» было 3 целых числа (Эми, Гейб, Ной), а не два (Эми, Гейб). Это связано с тем, что позиция индекса не сбрасывается при идентификации каждой группы. Кто-нибудь знает, как сбросить мою пользовательскую позицию индекса для каждой группы?
ОБНОВЛЕНИЕ 2 Кажется, я нашел ответ. Во-первых, сделайте дополнительную функцию, подобную этой:
public static bool BatchGroup(string ID, ref string priorID )
{
if (priorID != ID)
{
priorID = ID;
return true;
}
return false;
}
Во-вторых, обновите запрос LINQ следующим образом:
int i = 0;
string priorID = null;
var result = from q in input
orderby q.Age, q.ID
group q by new { q.ID, batch = (BatchGroup(q.ID, ref priorID) ? i=0 : ++i) / 3 };
Теперь он делает то, что я хочу. Я просто хочу, чтобы мне не нужна была эта отдельная функция!