0

我有一个包含两列的 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;
            }
        }
    }
}

假设我需要遵守这种输出格式,我该如何提高这种性能?

4

4 回答 4

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) 无关的数据,因此它不会改变我们的枚举行为。

于 2012-12-26T17:23:52.320 回答
0

最好在获取数据的地方添加生成 CalculatedID 的代码,但是,如果不可用,您可能希望避免每次发现重复时扫描整个表。您可以将 aDictionary用于已使用的键,如下所示:

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 以及用于生成键的最后一个字符。如果您有大量这些 JobDetailID,这可能会占用大量内存,但我怀疑除非您有数百万行要处理,否则您会遇到问题。

于 2012-12-26T16:49:41.320 回答
0

此方法一次性完成工作。例如,如果“JobDetailID”是整数而不是字符串,或者 DataTable 始终接收按“JobDetailID”排序的数据(您可以摆脱字典),您可以进一步优化它,但这里有一个草稿:

    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();
        }
    }
于 2014-05-06T14:48:25.357 回答
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"];
        }
    }
}
于 2012-12-26T17:02:53.997 回答