复合键的字典,在编译时未定义

时间:2016-04-20 14:25:57

标签: .net data-structures

我正在编写一个函数,它将采用某种输入数据结构(可能是一个DataTable;它仍然悬而未决,但重要的是它的结构在运行时才会被定义)并且分裂其中一个字符串列成为令牌列表。最终的结果将是一些数据结构(可能是一个词典,但同样,它仍然悬而未决),这将允许基于原始数据结构中的主键快速检索这些令牌列表。

通常你会使用一个元组作为复合键,但由于这是一个连接到数据库并获取任意表/查询的工具,我不能在这里采用这种方法。在我编写自己的CompoundKey类来处理这个问题之前,是否有适合这样做的内容在.NET中内置?这个应用程序中的某些地方会出现这种情况,而不仅仅是这种特殊的标记化功能。

这是一个非常粗略的概念,该方法将是什么样子,“对象”代表最终使用的复合键类。请注意,CompoundKey类实际上并不存在(Word),WordBreakChars可以是string[]char[],也可以在班级的其他地方准备。

public Dictionary<object, string[]> SplitTokens(DataTable table, string split_column) {
    Dictionary<object, string[]> Results = new Dictionary<object, string[]>();
    DataColumn[] KeyCols = table.PrimaryKey;
    if (KeyCols == null || KeyCols.Length == 0) {
        throw new ArgumentException("DataTable has no primary key.");
    }

    foreach (DataRow row in table.Rows) {
        string[] tokens = (row[split_column] as string ?? "").Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries);
        CompoundKey key = new CompoundKey();
        foreach (DataColumn col in KeyCols) {
            key.Add(col.ColumnName, row[col]);
            Results.Add(key, tokens);
        }
    }

    return Results;
}

2 个答案:

答案 0 :(得分:0)

在这种情况下,我会使用“键选择器”创建泛型函数。实际上,LINQ已经在.ToDictionary(的一个重载中提供了这个,您可以将它与现有代码结合使用,以大大简化方法

public Dictionary<T, string[]> SplitTokens<T>(DataTable table, string split_column, Func<DataRow, T> keySelector)
{
    Dictionary<T, string[]> Results;

    Results = table.AsEnumerable().ToDictionary(keySelector, 
              row => (row[split_column] as string ?? "").Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries));

    return Results;
}

现在调用者可以将他们想要的任何内容传递给keySelector,这将是结果字典的强类型数据类型。

答案 1 :(得分:0)

这不是特别花哨,但似乎它会起作用。

public class CompoundKey : IEquatable<CompoundKey>, IEnumerable {
    private object[] _Key;
    private int _Hash;
    private bool _Hashed;

    //Dictionary keys need to be immutable. DO NOT expect sane behavior if you modify items inside the key.
    public object this[int index] {
        get { return _Key[index]; }
    }

    public CompoundKey(params object[] Key) {
        _Key = Key;
        _Hashed = false;
    }

    public static implicit operator CompoundKey(object[] Key) {
        return new CompoundKey(Key);
    }

    public int Length { get { return _Key.Length; } }

    public override int GetHashCode() {
        if (!_Hashed) {
            _Hash = 0;
            foreach (object o in _Key) {
                if (o != null) {
                    _Hash ^= o.GetHashCode();
                }
            }
        }
        return _Hash;
    }

    public bool Equals(CompoundKey other) {
        if (other.GetHashCode() != _Hash) {
            return false;
        }
        if (other.Length != this.Length) {
            return false;
        }
        for (int i = 0; i < this.Length; i++) {
            if (other[i] != this[i]) {
                return false;
            }
        }

        return true;
    }

    public override bool Equals(object obj) {
        if (!(obj is CompoundKey)) {
            return false;
        }
        return this.Equals((CompoundKey)obj);
    }

    public static bool operator ==(CompoundKey a, CompoundKey b) {
        if ((object)a == null || (object)b == null) {
            return false;
        }
        return a.Equals(b);
    }

    public static bool operator !=(CompoundKey a, CompoundKey b) {
        return !(a == b);
    }

    public IEnumerator GetEnumerator() {
        return _Key.GetEnumerator();
    }
}

我添加了来自object[]的隐式转换,因此可以简化用法,将object[]直接输入字典。 (我知道我可以Linq离开使用.Select()构建密钥数组的内部for循环;我现在只是为了更容易调试而使它更加明确。)

    public Dictionary<CompoundKey, string[]> SplitTokens(DataTable table, string split_column) {
        Dictionary<CompoundKey, string[]> Results = new Dictionary<CompoundKey, string[]>();
        DataColumn[] key = table.PrimaryKey;
        Regex RemoveIgnoredCharacters = new Regex("[" + Regex.Escape(Ignore) + "]");

        char[] WordBreakChars = WordBreak.ToCharArray();

        for (int i = 0; i < table.Rows.Count; i++) {
            string split_value = RemoveIgnoredCharacters.Replace(table.Rows[i][split_column] as string ?? "", "");
            string[] tokens = split_value.Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries);
            object[] dictkey = new object[key.Length];
            for (int j = 0; j < key.Length; j++) {
                dictkey[j] = table.Rows[i][key[j].ColumnName];
            }
            Results.Add(dictkey, tokens);
        }

        return Results;
    }