识别C#列表中的唯一值

时间:2013-12-17 15:17:22

标签: c# linq

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

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相同;我在这里已经改变它以使其更清楚。

enter image description here

编辑:澄清问题

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

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

4 个答案:

答案 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()HashSetDictionary等。当然,替代方法是明确按不同属性进行分组 - 但感觉就像这样应该明智地实现平等。正如评论中所述,我敦促您将这些属性设为只读。

答案 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 + ",");