ProgramingTip

C #에서 사전 간의 동등성 테스트

bestdevel 2021. 1. 6. 20:53
반응형

C #에서 사전 간의 동등성 테스트


사전 키와 값이 같음 및 해시 메서드가 올바르게 구현되어 있다고 가정하면 두 사전의 같음을 테스트하는 가장 간결하고 효율적인 방법은 무엇입니까?

이 맥락에서 두 사전은 동일한 키 세트 (순서는 중요하지 않음)를 포함하는 경우 동일하다고 말하고 이러한 모든 키에 대해 값에 동의합니다.

여기에 내가 생각 해낸 몇 가지 방법이 있습니다 (아마 더 많을 것입니다).

public bool Compare1<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey,TValue> dic2)
{
    return dic1.OrderBy(x => x.Key).
        SequenceEqual(dic2.OrderBy(x => x.Key));
}

public bool Compare2<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Count == dic2.Count && 
        dic1.Intersect(dic2).Count().
        Equals(dic1.Count));
}

public bool Compare3<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Intersect(dic2).Count().
        Equals(dic1.Union(dic2).Count()));
}

dic1.Count == dic2.Count && !dic1.Except(dic2).Any();

그것은 평등이 의미하는 바에 달려 있습니다.

이 메서드는 두 사전이 동일한 값을 가진 동일한 키를 포함하는지 테스트합니다 (두 사전이 동일한 IEqualityComparer<TKey>구현을 사용한다고 가정 ).

public bool CompareX<TKey, TValue>(
    Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2)
{
    if (dict1 == dict2) return true;
    if ((dict1 == null) || (dict2 == null)) return false;
    if (dict1.Count != dict2.Count) return false;

    var valueComparer = EqualityComparer<TValue>.Default;

    foreach (var kvp in dict1)
    {
        TValue value2;
        if (!dict2.TryGetValue(kvp.Key, out value2)) return false;
        if (!valueComparer.Equals(kvp.Value, value2)) return false;
    }
    return true;
}

키 / 값 비교에 linq를 사용할 수 있습니다.

public bool Compare<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue dict2)
{
    IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;

    return  dict1.Count == dict2.Count &&
            dict1.Keys.All(key => dict2.ContainsKey(key) && valueComparer.Equals(dict1[key], dict2[key]));
}

@Allen의 대답 :

bool equals = a.Intersect(b).Count() == a.Union(b).Count()

배열에 관한 것이지만 IEnumerable<T>메서드가 사용되는 한도 사용할 수 있습니다 Dictionary<K,V>.


Except 메서드에 대한 smarthelp에서 읽은 내용에 따라 허용 된 대답이 정확하다고 생각했습니다. "값을 비교하기 위해 기본 같음 비교자를 사용하여 두 시퀀스의 집합 차이를 생성합니다." 그러나 나는 그것이 좋은 대답이 아니라는 것을 발견했습니다.

이 코드를 고려하십시오.

Dictionary<string, List<string>> oldDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Doe", "Jane"}}};
Dictionary<string, List<string>> newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Doe", "Jane"}}};

bool equal = oldDict.Count.Equals(newDict.Count) && !oldDict.Except(newDict).Any();
Console.WriteLine(string.Format("oldDict {0} newDict", equal?"equals":"does not equal"));
equal = oldDict.SequenceEqual(newDict);
Console.WriteLine(string.Format("oldDict {0} newDict", equal ? "equals" : "does not equal"));

Console.WriteLine(string.Format("[{0}]", string.Join(", ", 
    oldDict.Except(newDict).Select(k => 
        string.Format("{0}=[{1}]", k.Key, string.Join(", ", k.Value))))));

결과는 다음과 같습니다.

oldDict does not equal newDict
oldDict does not equal newDict
[001A=[John, Doe], 002B=[Frank, Abignale], 003C=[Doe, Jane]]

보시다시피 "oldDict"와 "newDict"는 정확히 동일하게 설정됩니다. 그리고 제안 된 솔루션이나 SequenceEqual 호출이 제대로 작동하지 않습니다. 지연 로딩을 사용하는 Except 또는 비교자가 Dictionary에 대해 설정되는 방식의 결과인지 궁금합니다. (하지만 구조와 참조 설명을 보면 그래야한다고 제안합니다.)

여기 내가 생각 해낸 해결책이 있습니다. 내가 사용한 규칙은 다음과 같습니다. 두 사전에 동일한 키가 포함되어 있고 각 키 일치 값이 있으면 두 사전이 동일합니다. 키와 값은 모두 동일한 순서대로 있어야합니다. 그리고 내 솔루션은 전체 키 세트를 반복하는 데 의존하기 때문에 가장 효율적이지 않을 수 있습니다.

private static bool DictionaryEqual(
    Dictionary<string, List<string>> oldDict, 
    Dictionary<string, List<string>> newDict)
{
    // Simple check, are the counts the same?
    if (!oldDict.Count.Equals(newDict.Count)) return false;

    // Verify the keys
    if (!oldDict.Keys.SequenceEqual(newDict.Keys)) return false;

    // Verify the values for each key
    foreach (string key in oldDict.Keys)
        if (!oldDict[key].SequenceEqual(newDict[key]))
            return false;

    return true;
}

또한 다음과 같은 경우 결과가 어떻게 변경되는지 확인하십시오. 키 순서가 동일하지 않습니다. (거짓 반환)

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"003C", new List<string> {"Doe", "Jane"}},
     {"002B", new List<string> {"Frank", "Abignale"}}};

및 키 순서가 일치하지만 값이 일치하지 않음 (false 반환)

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Jane", "Doe"}}};

시퀀스 순서가 중요하지 않으면 함수를 다음과 같이 변경할 수 있지만 성능 저하가있을 수 있습니다.

private static bool DictionaryEqual_NoSort(
    Dictionary<string, List<string>> oldDict,
    Dictionary<string, List<string>> newDict)
{
    // Simple check, are the counts the same?
    if (!oldDict.Count.Equals(newDict.Count)) return false;

    // iterate through all the keys in oldDict and
    // verify whether the key exists in the newDict
    foreach(string key in oldDict.Keys)
    {
        if (newDict.Keys.Contains(key))
        {
            // iterate through each value for the current key in oldDict and 
            // verify whether or not it exists for the current key in the newDict
            foreach(string value in oldDict[key])
                if (!newDict[key].Contains(value)) return false;
        }
        else { return false; }
    }

    return true;
}

newDict에 대해 다음을 사용하여 DictionaryEqual_NoSort가 있는지 확인하십시오 (DictionaryEquals_NoSort가 true를 반환 함).

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"003C", new List<string> {"Jane", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}}};     

@Nick Jones 답변 외에도 동일한 주문 방식으로 gethashcode를 구현해야합니다. 다음과 같이 제안합니다.

public override int GetHashCode()
{
        int hash = 13;
        var orderedKVPList = this.DictProp.OrderBy(kvp => kvp.key)
        foreach (var kvp in orderedKVPList)
        {
                 hash = (hash * 7)  + kvp.Key.GetHashCode();
                 hash = (hash * 7)  + kvp.value.GetHashCode();
        }
        return hash;
}

두 사전에 동일한 키가 있지만 순서가 다른 경우 동일한 것으로 간주되어야합니까? 그렇지 않은 경우 동시에 둘 모두를 통해 열거자를 실행하여 사전을 비교해야합니다. 이것은 아마도 한 사전을 통해 열거하고 다른 사전에서 각 요소를 찾는 것보다 빠를 것입니다. 동일한 사전이 동일한 순서로 요소를 가질 것이라는 사전 지식이 있다면 그러한 이중 열거가 아마도 갈 길일 것입니다.


영업 질문에 그것은 평등 테스트 키의 일치뿐만 아니라 자신의 가치 "단지 포함해야한다고 말 했는가 두 개의 사전을들이 키 (순서 중요하지)의 동일한 세트가 포함 된 경우 동일하고,라고하는 이러한 맥락에서를 위한 이러한 모든 키는 가치에 동의합니다 . "

내가 뭔가를 놓치고 있거나 표시된 답변 https://stackoverflow.com/a/3804852/916121 은 크기와 키가 같지만 값이 아닌지 확인합니까?

나는 이것을 대답 옆에 게시했지만 그것을 주석으로 추가하는 방법을 찾지 못했습니다.


중첩 사전 및 목록의 경우 여기에 몇 가지 아이디어를 결합하여이를 만들었습니다. https://gist.github.com/NullVoxPopuli/f95baaa48b4e9854dcfe (여기에 게시 할 코드가 너무 많음) ~ 100 줄

참조 URL : https://stackoverflow.com/questions/3804367/testing-for-equality-between-dictionaries-in-c-sharp

반응형