我创建了一个类,如下所示,表示复合主键模型:
public class PrimaryKeyModel
{
public string ColumnName { get; set; }
public string ColumnValue { get; set; }
public int RowNumber { get; set; } // always unique
}
它基本上表示组成主键的列的名称/值,加上该组合所属的行号;最初在电子表格中。
然后我将此模型放入List并使用电子表格中的数据填充它:
List<PrimaryKeyModel> primaryKeysList = new List<PrimaryKeyModel>;
我想检查primaryKeysList并查看它是否有任何重复值,如果有,我想知道这些值重复的行号。
我尝试过不同的方法,例如将此列表加载到HashSet,字典中并使用this solution here at this link,但不使用它。无论如何我可以解决这个问题。
感谢。
更新 - 这是一个示例数据显示。 UniqueColumnsModel与PrimaryKeyModel相同;我在这里已经改变它以使其更清楚。
编辑:澄清问题
我正在尝试从电子表格中导入数据(可以有多种类型(一种用于销售,一种用于报价..等))到数据库中。数据库中的配置表确定电子表格中的哪些列将构成目标表中的主键。我的任务是创建一个例程,在使用我的应用程序将电子表格数据上传(导入)到数据库之前验证它。我想要验证列设置为主键的组合,不包含任何重复数据,以便在插入时目标表中不违反主键约束。
此处提到的列表(PrimaryKeyModel)包含电子表格中列的名称(与其他列一起构成主键),电子表格中列的值以及电子表格中存在此值的行号。该列表通过foreach row / foreach列循环填充。所以我希望这能更好地阐述这个问题。
答案 0 :(得分:4)
GroupBy
适用于此:
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)
PrimaryKeyModel
推断出太多 - 我已经将其解释为主键的模型,并且你想要找到重复的主键。如果情况并非如此,我建议你重新考虑一下你的命名......那时候,D Stanley的回答可能可能你想要什么,但你应该认为ColumnName/ColumnValue
是“主要的”键“在这里 - 行号不是键的一部分,逻辑上。
原始回答
您似乎没有覆盖Equals(object)
或GetHashCode
- 这意味着每个对象都被视为与其他对象不同。你可能想要这样的东西:
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;
}
}
这假设您确实希望所有三个字段都相同 - 如果您仅关心RowNumber
,您可以简化这些实现(但此时它是一个奇怪的主键)。
之后,您可以使用Distinct()
,HashSet
或Dictionary
等。当然,替代方法是明确按不同属性进行分组 - 但感觉就像这样应该明智地实现平等。正如评论中所述,我敦促您将这些属性设为只读。
答案 2 :(得分:2)
如果你的班级代表这种结构:
ColumnName ColumnValue RowNumber
Id 3 1
Id2 1 1
Id 1 2
Id2 2 2
Id 3 3
Id2 1 3 //duplicate
然后到目前为止所有其他答案都是不正确的,您需要以不同方式进行,逐行编号,然后逐个比较每个字段。因为相等是commutative,我们可以稍微加快循环,所以我们不会比较每个项目两次。
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;
}
答案 3 :(得分:0)
这是对我有用的最终解决方案。这确保了重复项不存在于列表的行中,即列表列表中。它基本上将列表的内容倒入一个hashset,如果列表中已经存在新添加的项,则返回false:
感谢所有为解决上述问题做出贡献的人!
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突出显示的问题,只需确保在连接主键时;用昏迷或0来分隔它们,以便突出显示的场景不会发生..所以做这样的事情:
primaryKey.Append(currentValue + ",");