Это можно сделать с помощью LINQ (который, как я полагаю, вам нужен, поскольку вы включили тег LINQ
), но не в том, что я бы рассмотрел как способ nice.
Учитывая два DataTable
объекты с указанными выше форматов и данных, названный grouped
и detail
, вот выражение LINQ, который сшивает данные вместе так, как вы хотите:
IEnumerable<object[]> qry =
(
from DataRow rDetail in detail.Rows
let dgrp = detail.Rows.IndexOf(rDetail)/10
join DataRow rGroup in grouped.Rows
on dgrp equals grouped.Rows.IndexOf(rGroup)
orderby rDetail["RefNo"]
select new object[] {
rGroup["Shipment"], rGroup["Line"], rGroup["Remarks"], rGroup["ItemCode"], rGroup["TotalQty"],
rDetail["RefNo"], rDetail["Quantity"], rDetail["Weight"], rDetail["From"]
}
);
Теперь вам нужно другой DataTable
, чтобы свести эти результаты на:
DataTable res = new DataTable();
res.Columns.Add("Shipment", typeof(string));
res.Columns.Add("Line", typeof(Int32));
res.Columns.Add("Remarks", typeof(string));
res.Columns.Add("ItemCode", typeof(string));
res.Columns.Add("TotalQty", typeof(Int32));
res.Columns.Add("RefNo", typeof(string));
res.Columns.Add("Quantity", typeof(Int32));
res.Columns.Add("Weight", typeof(Int32));
res.Columns.Add("From", typeof(string));
И, наконец, заполните res
таблица с результатами запроса LINQ:
foreach (object[] rowdata in qry)
res.Rows.Add(rowdata);
Код выше работает для этого конкретного набора данных, но я не могу обещать вам ничего больше, чем это. Он сильно зависит от порядка строк в исходных таблицах, и потому что я использую DataTable.Rows.IndexOf
, чтобы получить заказ, вполне возможно, что это будет очень медленным в больших сборниках данных.
Но тогда вы уже используете DataTable
s вместо правильно подобранной коллекции, поэтому все ставки в любом случае не влияют на производительность и код.
Это один случай, когда я предлагаю не с помощью LINQ для выполнения задачи. ИМХО это лучше было бы сделать как итеративный цикл, а не запрос. Вы не получаете много (если есть) улучшения по сравнению с итеративной версией, вы теряете много ясности, и у вас есть всевозможные развлечения, которые можно настроить прямо перед тем, как вы сможете их использовать.
И потому, что я не могу оставить хорошо достаточно один, вот полный (длинный, много кода) решение с использованием комбинации LINQ, классов, чтобы держать данные, обрабатываемые и итерации для создания таблицы:
public DataTable MergeShippingData(DataTable groupTable, DataTable detailTable)
{
// convert group table to array of GroupEntry objects
var groupList =
(
from DataRow grouprow in groupTable.Rows
let ent = GroupEntry.FromRow(grouprow)
where ent != null
select ent
).ToArray();
// convert detail table to sequence of DetailEntry objects
var detailSeq =
from DataRow detailrow in detailTable.Rows
let ent = DetailEntry.FromRow(detailrow)
where ent != null
select ent;
// Create output DataTable
DataTable output = CreateOutputTable();
// Process all detail lines into shippings
foreach (var detail in detailSeq)
{
// Find available shipping group for the item code with enough remaining capacity
var grp = groupList.First (g => g.ItemCode == detail.ItemCode && g.Remainder >= detail.Quantity);
if (grp == null)
throw new Exception("No available shipping found for detail item...");
// update remaining space in shipping group
grp.Remainder -= detail.Quantity;
// add data to output table
output.Rows.Add(new object[] {
grp.Shipment, grp.Line, grp.Remarks, grp.ItemCode, grp.TotalQty,
detail.RefNo, detail.Quantity, detail.Weight, detail.From
});
}
return output;
}
// Class to hold the shipping groups while processing
public class GroupEntry
{
// fields from source DataTable
public string ItemCode;
public int TotalQty;
public string Shipment;
public string Remarks;
public int Line;
// process variable, holds remaining quantity value
public int Remainder;
// Convert DataRow into GroupEntry
public static GroupEntry FromRow(DataRow r)
{
try
{
return new GroupEntry
{
ItemCode = r.Field<string>(0),
TotalQty = r.Field<int>(1),
Shipment = r.Field<string>(2),
Remarks = r.Field<string>(3),
Line = r.Field<int>(4),
Remainder = r.Field<int>(1)
};
}
catch { }
return null;
}
}
// Class to hold shipping Detail records during processing
public class DetailEntry
{
public string RefNo;
public string ItemCode;
public int Quantity;
public int Weight;
public string From;
// Convert DataRow into DetailEntry
public static DetailEntry FromRow(DataRow r)
{
try
{
return new DetailEntry
{
RefNo = r.Field<string>(0),
ItemCode = r.Field<string>(1),
Quantity = r.Field<int>(2),
Weight = r.Field<int>(3),
From = r.Field<string>(4)
};
}
catch { }
return null;
}
}
// Create output DataTable
public DataTable CreateOutputTable()
{
DataTable res = new DataTable();
res.Columns.Add("Shipment", typeof(string));
res.Columns.Add("Line", typeof(Int32));
res.Columns.Add("Remarks", typeof(string));
res.Columns.Add("ItemCode", typeof(string));
res.Columns.Add("TotalQty", typeof(Int32));
res.Columns.Add("RefNo", typeof(string));
res.Columns.Add("Quantity", typeof(Int32));
res.Columns.Add("Weight", typeof(Int32));
res.Columns.Add("From", typeof(string));
return res;
}
Включите обработку ошибок, и вы хорошо пойдете.
Между вашим входом и выходом нет очевидной связи. Ожидаете ли вы, что элементы из таблицы «Деталь» должны быть привязаны к записи из таблицы «Группировка» на основе общей суммы поля «Количество»? – Corey
Да. В принципе, элементы в источнике 1 и 2 одинаковы. Но мне нужно напомнить, что «Отгрузка», «Заметки» и «Линия» будут назначаться каждые 10 позиций одним и тем же кодом. –