比较两个字典(Dictionary<string, List<string>>)

3

我需要检查两个类型为Dictionary<string, List<string>>的字典是否相同。到目前为止,我有以下代码:

// dictionaries are already defined, so no parameters are required
private bool DictionariesEqual()
{
   return dictionary1.SequenceEqual(dictionary2);
}

我假设这只检查键和值的顺序是否相同,但这不是我想要的,因为只有每个键和值的名称才是重要的,顺序并不重要
有没有一种方法可以检查字典中不相等的字符串,并在找到第一个不匹配时返回false? 编辑 感谢您所有人的帮助,但值更改仍未被识别。
更具体地说,这是函数(感谢@juharr)以及我如何使用它:
private bool DictionaryContentChanged()
{
    if(synonymDictionary.Count != temporaryDictionary.Count ||
       synonymDictionary.Keys.Except(temporaryDictionary.Keys).Any() ||
       !synonymDictionary.Join(
            temporaryDictionary,
            kvp => kvp.Key,
            kvp => kvp.Key,
            (kvp1, kvp2) => new
            {
                l1 = kvp1.Value,
                l2 = kvp2.Value
            })
            .All(a => a.l1.Count == a.l2.Count && !a.l1.Except(a.l2).Any()))
            return true;
        return false;
{

我将它放入一个更新函数中,该函数以一定的时间间隔重复检查变化:

void Update(int interval)
{
    while(!this.IsDisposed)
    {
        Set.Timer(interval);

        if(DictionaryContentChanged())
        {
            MessageBox.Show("Changes detected");
        }
        else
        {
            // move on
        }
    }
}

当我添加、删除或更改一个值时,什么都不会发生。如果一个键被改变或key.Count被改变,则消息框才会出现。这可能与比较字典的函数有关吗?
请记住,每个KeyValuePair的值表示一个通用列表。

请问为什么您取消了我的答案? - MakePeaceGreatAgain
抱歉,我匆忙地接受了它,当我意识到字典键更改已被注册时,但当我意识到值更改未被您的比较看到时,我取消了您的答案。 - betaFlux
你的列表中的值实际上是字符串还是任意对象?如果是前者,我的解决方案应该可以工作;否则,你应该在每种可能的对象类型上实现Equals(和GetHashCode)。 - MakePeaceGreatAgain
两个字典当然都包含普通字符串和字符串列表。相关的类相当大,大约有900行代码。我想我得再深入挖掘一下,看看是什么导致了这个问题。 - betaFlux
3个回答

3

没有测试过,但我猜以下内容可能有效:

if (dictionary1.Count != dictionary2.Count) return false;
foreach (var kv in dictionary1) {
    if (!dictionary2.ContainsKey(kv.Key) return false;

    List list = dictionary2[kv.Key];
    if (!list.Count != dictionary1[kv.Key].Count) return false;

    foreach(var value in kv.Value) {
        if(!list.Contains(value)) return false;
    }
}
return true;

编辑:我添加了一些长度检查。


接受了,因为它还能识别字符串的更改,而其他方法则不能。谢谢! - betaFlux
+1 你应该加入一些长度检查,因为一个更长的dictionary2/dictionary2列表可能会通过这些测试。 - Alex K.
@Alex K. 我可以问一下我应该如何做吗?我应该比较两个字典条目的总和吗? - betaFlux
1
计数器应该放在循环之前,它不会改变! - Alex K.
@Richard 前一行代码检查了键是否存在,如果不存在则返回 false。 - juharr
@juharr 抱歉,我读得太快了!请原谅。由于它已经变得相当无用,我删除了我的评论。 - AFract

1
这是一个 Linq 版本。
dic1.Count == dic2.Count &&
!dic1.Keys.Except(dic2.Keys).Any() &&
dic1.Join(
    dic2,
    kvp => kvp.Key,
    kvp => kvp.Key,
    (kvp1, kvp2) => new
    {
        l1 = kvp1.Value,
        l2 = kvp2.Value
    })
    .All(a => a.l1.Count == a.l2.Count && !a.l1.Except(a.l2).Any())

首先,它确保字典具有相同的项目数,然后确保它们具有相同的键。然后按照它们的键将它们连接起来,并确保具有相同键的所有列表具有相同的计数和相同的内容。
编辑:
以下是获取相反返回值的一些方法。
将整个内容用()括起来,并在前面加上一个!。
!(dic1.Count == dic2.Count &&
!dic1.Keys.Except(dic2.Keys).Any() &&
dic1.Join(
    dic2,
    kvp => kvp.Key,
    kvp => kvp.Key,
    (kvp1, kvp2) => new
    {
        l1 = kvp1.Value,
        l2 = kvp2.Value
    })
    .All(a => a.l1.Count == a.l2.Count && !a.l1.Except(a.l2).Any()))

如果您不分发,则会得到这个结果,请注意,您需要将 ands (&&) 更改为 ors (||)。
dic1.Count != dic2.Count ||
dic1.Keys.Except(dic2.Keys).Any() ||
!dic1.Join(
    dic2,
    kvp => kvp.Key,
    kvp => kvp.Key,
    (kvp1, kvp2) => new
    {
        l1 = kvp1.Value,
        l2 = kvp2.Value
    })
    .All(a => a.l1.Count == a.l2.Count && !a.l1.Except(a.l2).Any())

这是基于德摩根定律,它说明

Not(A And B) = Not A Or Not B

另一个选择是将您的方法从DictionaryContentChanged更改为DictionaryContentSame,并在需要时仅否定它。

很棒的Linq代码片段!但是你和HimBromBeeres的方法无法识别键(名称、计数)的值List<>中的任何更改。通过对你的代码片段进行小修改,这个问题可以解决吗? - betaFlux
@betaFlux,我不明白你的意思。这将确保具有相同键的列表具有相同的值,而不考虑顺序。这不是你想要的吗? - juharr
请再看一下我的问题。我添加了一些信息。 - betaFlux
我实际上将所有的相等检查都改为了相反的,以获得相反的结果。至于值,仍然没有运气。 - betaFlux
1
@betaFlux 请查看我的编辑,了解如何否定逻辑表达式。 - juharr
显示剩余3条评论

-1

对两个字典进行排序并进行比较

bool result2 = dictionary1.OrderBy(r=>r.Key).SequenceEqual(dictionary2.OrderBy(r=>r.Key));

无法生效,因为它通过引用而非内容比较列表。 - juharr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接