Identifying Unique Values in a C# List
我创建了一个类,如下所示,来表示复合主键模型:
1 2 3 4 5 6 | public class PrimaryKeyModel { public string ColumnName { get; set; } public string ColumnValue { get; set; } public int RowNumber { get; set; } // always unique } |
它基本上代表了组成主键的列的名称/值,加上这个组合所属的行号;最初在电子表格中。
然后我把这个模型放在一个列表中,并用电子表格中的数据填充它:
1 |
我想检查primaryKeysList,看看它是否有任何重复的值,如果有,我想知道这些值重复的行号。
我尝试了不同的方法,例如将此列表加载到 HashSet、字典中,并在此链接上使用此解决方案,但没有一个有效。无论如何我可以解决这个问题。
谢谢。
更新 - 这是一个示例数据显示。 UniqueColumnsModel 与 PrimaryKeyModel 相同;我已经在这里更改了它以使其更清晰。
编辑:澄清问题
我正在尝试将电子表格中的数据(可以有多种类型(一种用于销售,一种用于报价等))导入数据库。数据库中的配置表确定电子表格中的哪些列将构成目标表中的主键。我的任务是创建一个例程,在使用我的应用程序将电子表格数据上传(导入)到数据库之前对其进行验证。我想验证设置为主键组合的列不包含任何重复数据,以便在插入时在目标表中不违反主键约束..
这里提到的列表(PrimaryKeyModel)包含电子表格中列的名称(与其他列一起构成主键)、电子表格中列的值以及电子表格中该值所在的行号。该列表通过 foreach 行/ foreach 列循环填充。所以我希望这能更好地阐述这个问题。
1 2 3 | primaryKeysList.GroupBy(pk => new {pk.ColumnName, pk.ColumnValue}) .Where(g => g.Count() > 1) .SelectMany(g => g); // flatten the groups into a single list |
如果你的类代表这种结构:
1 2 3 4 5 6 7 | ColumnName ColumnValue RowNumber Id 3 1 Id2 1 1 Id 1 2 Id2 2 2 Id 3 3 Id2 1 3 //duplicate |
那么到目前为止所有其他答案都不正确,您需要以不同的方式进行操作,按行号分组,然后逐个比较每个字段。因为相等是可交换的,所以我们可以稍微加快循环速度,这样我们就不会对每个项目进行两次比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | List<PrimaryKeyModel> keys = new List<PrimaryKeyModel>() { new PrimaryKeyModel("Id","3", 1), new PrimaryKeyModel("Id2","1", 1), new PrimaryKeyModel("Id","1", 2), new PrimaryKeyModel("Id2","1", 2), new PrimaryKeyModel("Id","3", 3), new PrimaryKeyModel("Id2","1", 3), }; var groupedKeys = keys.OrderBy(pk => pk.ColumnName).GroupBy(k => k.RowNumber).ToList(); HashSet<int> duplicateRowNumbers = new HashSet<int>(); for (int i = 0; i < groupedKeys.Count - 1; i++) { for (int j = i + 1; j < groupedKeys.Count; j++) { if (AreTheSame(groupedKeys[i], groupedKeys[j])) { duplicateRowNumbers.Add(groupedKeys[i].First().RowNumber); duplicateRowNumbers.Add(groupedKeys[j].First().RowNumber); } } } private static bool AreTheSame(IEnumerable<PrimaryKeyModel> a, IEnumerable<PrimaryKeyModel> b) { var leftEnumerator = a.GetEnumerator(); var rightEnumerator = b.GetEnumerator(); while (leftEnumerator.MoveNext() | rightEnumerator.MoveNext()) { if (leftEnumerator.Current == null) return false; if (rightEnumerator.Current == null) return false; if (leftEnumerator.Current.ColumnValue != rightEnumerator.Current.ColumnValue) return false; } return true; } |
编辑:我很可能误读了这个问题,并且从您的类名
原答案
您似乎没有覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public sealed class PrimaryKeyModel : IEquatable<PrimaryKeyModel> { // TODO: Make these read-only (mutable keys are a bad idea...) public string ColumnName { get; set; } public string ColumnValue { get; set; } public int RowNumber { get; set; } public override bool Equals(object other) { return Equals(other as PrimaryKeyModel); } public bool Equals(PrimaryKeyModel other) { return other != null && ColumnName == other.ColumnName && ColumnValue == other.ColumnValue && RowNumber == other.RowNumber; } public override int GetHashCode() { int hash = 23; hash = hash * 31 + ColumnName == null ? 0 : ColumnName.GetHashCode(); hash = hash * 31 + ColumnValue == null ? 0 : ColumnValue.GetHashCode(); hash = hash * 31 + RowNumber; return hash; } } |
这是假设您真的希望所有三个字段都相同 - 如果您只关心
之后,您可以使用
这是对我有用的最终解决方案。这确保了列表的一行中不存在重复项,即列表列表。它基本上将列表的内容倒入一个哈希集中,如果列表中已经存在新添加的项目,则返回 false:
感谢所有为解决上述问题做出贡献的人!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | HashSet<string> primaryKeyChecker = new HashSet<string>(); foreach (var row in rows) { StringBuilder primaryKey = new StringBuilder(); //Get rowCount; foreach (var column in columns) { (if column is a composite of a primaryKey) { get column value; append it to stringBuilder to form the primaryKey } } var addOutcome = primaryKeyChecker.Add(primaryKey.ToString()); if (!addOutcome) { //Report a duplicate record and give the rowNumber where this occured. } } |
更新
要解决下面@Bas 突出显示的问题,只需确保在连接主键时;用 coma 或 0 分隔它们,以便突出显示的场景不会发生.. 所以做这样的事情:
1 | primaryKey.Append(currentValue +","); |