在DataTable中分配值的更有效方法?

时间:2012-12-26 16:34:17

标签: c# performance linq datatable

我有一个包含两列的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 个答案:

答案 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();
        }
    }