识别C#列表中的唯一值

我创建了一个类,如下所示,表示复合主键模型:

public class PrimaryKeyModel { public string ColumnName { get; set; } public string ColumnValue { get; set; } public int RowNumber { get; set; } // always unique } 

它基本上表示组成主键的列的名称/值,加上该组合所属的行号; 最初在电子表格中。

然后我将此模型放在List中,并使用电子表格中的数据填充它:

 List primaryKeysList = new List; 

我想检查primaryKeysList并查看它是否有任何重复值,如果有,我想知道这些值重复的行号。

我已尝试过不同的方法,例如将此列表加载到HashSet,字典中以及在此链接中使用此解决方案但不起作用。 无论如何,我可以解决这个问题。

谢谢。

更新 – 这是一个示例数据显示。 UniqueColumnsModel与PrimaryKeyModel相同; 我在这里改变它以使它更清楚。

在此处输入图像描述

编辑:澄清问题

我试图从电子表格(可以有多种类型(一个用于销售,一个用于报价..等))导入数据库中的数据。 数据库中的配置表确定电子表格中的哪些列将构成目标表中的主键。 我的任务是创建一个例程,在使用我的应用程序将电子表格数据上传(导入)到数据库之前validation它。 我想要validation列设置为主键的组合,不包含任何重复数据,以便在插入时的目标表中不违反主键约束。

此处提到的列表(PrimaryKeyModel)包含电子表格中列的名称(与其他列一起构成主键),电子表格中列的值以及电子表格中存在该值的行号。 该列表通过foreach row / foreach列循环填充。 所以我希望这能更好地阐述这个问题。

如果你的类代表这种结构:

 ColumnName ColumnValue RowNumber Id 3 1 Id2 1 1 Id 1 2 Id2 2 2 Id 3 3 Id2 1 3 //duplicate 

然后到目前为止所有其他答案都是不正确的,您需要以不同方式进行,逐行编号,然后逐个比较每个字段。 因为相等是可交换的,所以我们可以稍微加快循环,所以我们不会比较每个项目两次。

 List keys = new List() { 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 duplicateRowNumbers = new HashSet(); 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 a, IEnumerable 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; } 

GroupBy适用于此:

 primaryKeysList.GroupBy(pk => new {pk.ColumnName, pk.ColumnValue}) .Where(g => g.Count() > 1) .SelectMany(g => g); // flatten the groups into a single list 

编辑:我可能误解了这个问题,并从你的类名推断出太多的是PrimaryKeyModel – 我已经将其解释为主键的模型,并且你想要找到重复的主键。 如果不是这样,我建议你重新考虑你的命名……那时,D Stanley的回答可能就是你想要的,但是你应该把ColumnName/ColumnValue视为这里的“主键” – 行号不是逻辑上,关键的一部分。


原始答案

您似乎没有覆盖Equals(object)GetHashCode – 这意味着每个对象被认为与其他对象不同。 你可能想要这样的东西:

 public sealed class PrimaryKeyModel : IEquatable { // 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; } } 

这假设你真的希望所有三个字段都是相同的 – 如果你关心RowNumber ,你可以简化那些实现(但那时它是一个奇怪的主键)。

之后,您可以使用Distinct()HashSetDictionary等。当然,另一种方法是通过不同的属性明确分组 – 但感觉这应该明智地实现平等。 正如评论中所述,我敦促您将属性设为只读。

这是对我有用的最终解决方案。 这确保了列表的行(即列表列表)中不存在重复项。 它基本上将列表的内容倒入一个hashset,如果列表中已经存在新添加的项,则返回false:

感谢所有为解决上述问题做出贡献的人!

 HashSet primaryKeyChecker = new HashSet(); 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突出显示的问题,只需确保连接主键时; 用昏迷或0来分隔它们,以便突出显示的场景不会发生..所以做这样的事情:

  primaryKey.Append(currentValue + ",");