我有一个包含两列的DataTable:JobDetailID和CalculatedID。 JobDetailID并不总是唯一的。我希望给定的JobDetailID的一个/第一个CalculatedID实例是JobDetailID +“A”,当有多个具有相同JobDetailID的行时,我希望连续的行是JobDetailID +“B”,“C”等。具有相同JobDetailID的行不超过四行或五行。
我目前实现如下,但速度慢得令人无法接受:
private void AddCalculatedID(DataTable data)
{
var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
data.Columns.Add(calculatedIDColumn);
data.Columns["CalculatedID"].SetOrdinal(0);
var enumerableData = data.AsEnumerable();
foreach (DataRow row in data.Rows)
{
var jobDetailID = row["JobDetailID"].ToString();
// Give calculated ID of JobDetailID + A, B, C, etc. for multiple rows with same JobDetailID
int x = 65; // ASCII value for A
string calculatedID = jobDetailID + (char)x;
while (string.IsNullOrEmpty(row["CalculatedID"].ToString()))
{
if ((enumerableData
.Any(r => r.Field<string>("CalculatedID") == calculatedID)))
{
calculatedID = jobDetailID + (char)x;
x++;
}
else
{
row["CalculatedID"] = calculatedID;
break;
}
}
}
}
假设我需要遵循这种输出格式,我该如何改善这种性能?
答案 0 :(得分:0)
最好在获取数据的位置添加用于生成CalculatedID的代码,但是,如果不可用,则可能希望每次找到重复项时都避免扫描整个表。您可以对使用的密钥使用Dictionary
,如下所示:
private void AddCalculatedID(DataTable data)
{
var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
data.Columns.Add(calculatedIDColumn);
data.Columns["CalculatedID"].SetOrdinal(0);
Dictionary<string, string> UsedKeyIndex = new Dictionary<string, string>();
foreach (DataRow row in data.Rows)
{
string jobDetailID = row["JobDetailID"].ToString();
string calculatedID;
if (UsedKeyIndex.ContainsKey(jobDetailID))
{
calculatedID = jobDetailID + 'A';
UsedKeyIndex.Add(jobDetailID, 'A');
}
else
{
char nextKey = UsedKeyIndex[jobDetailID].Value+1;
calculatedID = jobDetailID + nextKey;
UsedKeyIndex[jobDetailID] = nextKey;
}
row["CalculatedID"] = calculatedID;
}
}
这将基本上交换内存的速度,因为它将缓存所有使用的JobDetailID以及用于生成的密钥的最后一个char。如果你有很多这些JobDetailID,这可能会占用大量内存,但我怀疑你会遇到问题,除非你有数百万行要处理。
答案 1 :(得分:0)
如果我理解你为行设置CalculatedID的想法,那么下面的算法可以解决问题,它的复杂性是线性的。最重要的部分是data.Select("","JobDetailID")
,在那里我得到一个排序的行列表。
我自己没有编译,因此可能存在语法错误。
private void AddCalculatedID(DataTable data)
{
var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
data.Columns.Add(calculatedIDColumn);
data.Columns["CalculatedID"].SetOrdinal(0);
int jobDetailID = -1;
int letter = 65;
foreach (DataRow row in data.Select("","JobDetailID"))
{
if((int)row["JobDetailID"] == jobDetailID)
{
row["CalculatedID"] = row["JobDetailID"].ToString() + (char)letter;
letter++;
}
else
{
letter = 65;
jobDetailID = (int)row["JobDetailID"];
}
}
}
答案 2 :(得分:0)
您将此标记为LINQ,但您使用的是迭代方法。可能最好的方法是使用两者的组合,迭代每个“分组”并为分组中的每一行分配计算的ID。
foreach (var groupRows in data.AsEnumerable().GroupBy(d => d["JobDetailID"].ToString()))
{
if(string.IsNullOrEmpty(groupRows.Key))
continue;
// We now have each "grouping" of duplicate JobDetailIDs.
int x = 65; // ASCII value for A
foreach (var duplicate in groupRows)
{
string calcID = groupRows.Key + ((char)x++);
duplicate["CalculatedID"] = calcID;
//Can also do this and achieve same results.
//duplicate["CalculatedID"] = groupRows.Key + ((char)x++);
}
}
您要做的第一件事就是对要重复的列进行分组。您将迭代这些分组中的每一个,并为每个分组重置后缀值。对于分组中的每一行,您将获得计算的ID(同时递增后缀值)并将ID分配回重复行。作为旁注,我们正在改变我们在这里列举的项目,这通常是一件坏事。但是,我们正在更改与枚举声明(GroupBy)无关的数据,因此它不会改变枚举的行为。
答案 3 :(得分:0)
此方法可以一次性完成工作。例如,你可以进一步优化它,例如&#34; JobDetailID&#34;是一个整数而不是一个字符串,或者如果DataTable总是接收按&#34; JobDetailID&#34;排序的数据。 (你可以摆脱字典),但这是一个草案:
private static void AddCalculatedID(DataTable data)
{
data.BeginLoadData();
try
{
var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
data.Columns.Add(calculatedIDColumn);
data.Columns["CalculatedID"].SetOrdinal(0);
var jobDetails = new Dictionary<string, int>(data.Rows.Count);
foreach (DataRow row in data.Rows)
{
var jobDetailID = row["JobDetailID"].ToString();
int lastSuffix;
if (jobDetails.TryGetValue(jobDetailID, out lastSuffix))
{
lastSuffix++;
}
else
{
// ASCII value for A
lastSuffix = 65;
}
row["CalculatedID"] = jobDetailID + (char)lastSuffix;
jobDetails[jobDetailID] = lastSuffix;
}
}
finally
{
data.EndLoadData();
}
}