如何在C#中比较两个词典

时间:2012-03-03 15:41:30

标签: c# algorithm data-structures collections

我有两个Generic Dictionaries.Both有相同的键。但是值可以不同。我想比较第二个字典和第一个字典。如果值之间存在差异,我想将这些值存储在单独的字典中。

1st Dictionary
------------
key       Value

Barcode   1234566666
Price     20.00


2nd Dictionary
--------------
key       Value

Barcode   1234566666
Price     40.00


3rd Dictionary
--------------
key       Value

Price     40

可以任何人给我一个最好的算法来做这个。我写了一个算法,但它有很多循环。我正在寻求一个简短而有效的想法。也像使用LINQ查询表达式或LINQ lamda表达式的解决方案.I我正在使用.Net Framework 3.5和C#。我发现了一些关于Except()方法的东西。但不幸的是我无法理解该方法发生了什么。如果有人解释建议的算法,那就太好了。我总是想学习:)。

由于 萨波。

12 个答案:

答案 0 :(得分:32)

如果您已经检查过密钥是否相同,则可以使用:

var dict3 = dict2.Where(entry => dict1[entry.Key] != entry.Value)
                 .ToDictionary(entry => entry.Key, entry => entry.Value);

要解释,这将:

  • dict2
  • 中的键/值对进行迭代
  • 对于每个条目,在dict1中查找值并过滤掉两个值相同的条目
  • 从其余条目(即dict1值不同的条目)中形成字典,方法是从dict2中显示每对中的键和值。

请注意,这可以避免依赖于KeyValuePair<TKey, TValue>的平等 - 可能可以依赖它,但我个人觉得这更清楚。 (当你为字典键使用自定义相等比较器时它也会起作用 - 尽管你也需要将它传递给ToDictionary。)

答案 1 :(得分:20)

尝试:

dictionary1.OrderBy(kvp => kvp.Key)
           .SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

答案 2 :(得分:11)

你提到两个词典都有相同的键,所以如果这个假设是正确的,你就不需要任何花哨的东西:

        foreach (var key in d1.Keys)
        {
            if (!d1[key].Equals(d2[key]))
            {
                d3.Add(key, d2[key]);
            }
        }

或者我误解了你的问题?

答案 3 :(得分:11)

检查任何差异,

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

以下代码返回所有不同的值

dic1.Except(dic2) 

答案 4 :(得分:2)

您应该可以在键上加入它们并选择这两个值。然后,您可以根据值是相同还是不同进行过滤。最后,您可以使用键和第二个值将集合转换为字典。

  var compared = first.Join( second, f => f.Key, s => s.Key, (f,s) => new { f.Key, FirstValue = f.Value, SecondValue = s.Value } )
                      .Where( j => j.FirstValue != j.SecondValue )
                      .ToDictionary( j => j.Key, j => j.SecondValue );

使用循环也不应该太糟糕。我怀疑它们会有类似的性能特征。

  var compared = new Dictionary<string,object>();
  foreach (var kv in first)
  {
      object secondValue;
      if (second.TryGetValue( kv.Key, out secondValue ))
      {
            if (!object.Equals( kv.Value, secondValue ))
            {
                compared.Add( kv.Key, secondValue );
            }
      }
  }

答案 5 :(得分:2)

假设两个词典都有相同的键,最简单的方法是

var result = a.Except(b).ToDictionary(x => x.Key, x => x.Value);

修改

请注意,a.Except(b)提供的结果与b.Except(a)

不同
a.Except(b): Price     20
b.Except(a): Price     40

答案 6 :(得分:2)

var diff1 = d1.Except(d2);
var diff2 = d2.Except(d1);
return diff1.Concat(diff2);

编辑:如果您确定所有密钥都相同,则可以执行以下操作:

var diff = d2.Where(x=>x.Value != d1[x.Key]).ToDictionary(x=>x.Key, x=>x.Value);

答案 7 :(得分:0)

  

将对象转换为字典然后按照set concept减去它们,结果项应该为空,以防它们相同。

 public static IDictionary<string, object> ToDictionary(this object source)
    {
        var fields = source.GetType().GetFields(
            BindingFlags.GetField |
            BindingFlags.Public |
            BindingFlags.Instance).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source) ?? string.Empty
        );

        var properties = source.GetType().GetProperties(
            BindingFlags.GetField |
            BindingFlags.GetProperty |
            BindingFlags.Public |
            BindingFlags.Instance).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source, null) ?? string.Empty
        );

        return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
    }
    public static bool EqualsByValue(this object source, object destination)
    {
        var firstDic = source.ToFlattenDictionary();
        var secondDic = destination.ToFlattenDictionary();
        if (firstDic.Count != secondDic.Count)
            return false;
        if (firstDic.Keys.Except(secondDic.Keys).Any())
            return false;
        if (secondDic.Keys.Except(firstDic.Keys).Any())
            return false;
        return firstDic.All(pair =>
          pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
        );
    }
    public static bool IsAnonymousType(this object instance)
    {

        if (instance == null)
            return false;

        return instance.GetType().Namespace == null;
    }
    public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
    {
        var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
        foreach (var item in source.ToDictionary())
        {
            var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
            if (item.Value.IsAnonymousType())
                return item.Value.ToFlattenDictionary(key, propsDic);
            else
                propsDic.Add(key, item.Value);
        }
        return propsDic;
    }
originalObj.EqualsByValue(messageBody); // will compare values.

source of the code

答案 8 :(得分:0)

在最新的C#版本中,您可以尝试

        public static Dictionary<TK, TV> ValueDiff<TK, TV>(this Dictionary<TK, TV> dictionary,
            Dictionary<TK, TV> otherDictionary)
        {
            IEnumerable<(TK key, TV otherValue)> DiffKey(KeyValuePair<TK, TV> kv)
            {
                var otherValue = otherDictionary[kv.Key];
                if (!Equals(kv.Value, otherValue))
                {
                    yield return (kv.Key, otherValue);
                }
            }

            return dictionary.SelectMany(DiffKey)
                .ToDictionary(t => t.key, t => t.otherValue, dictionary.Comparer);
        }

我不确定SelectMany始终是最快的解决方案,但这是仅选择相关项目并在同一步骤中生成结果条目的一种方法。遗憾的是,C#在lambda中不支持yield return,尽管我可以构造单个或不构造任何项目集合,但我选择使用内部函数。

哦,正如您所说的,键是相同的,可能可以订购它们。然后,您可以使用Zip

        public static Dictionary<TK, TV> ValueDiff<TK, TV>(this Dictionary<TK, TV> dictionary,
            Dictionary<TK, TV> otherDictionary)
        {
            return dictionary.OrderBy(kv => kv.Key)
                .Zip(otherDictionary.OrderBy(kv => kv.Key))
                .Where(p => !Equals(p.First.Value, p.Second.Value))
                .ToDictionary(p => p.Second.Key, p => p.Second.Value, dictionary.Comparer);
        }

我个人倾向于不使用Linq,而是使用简单的foreach,例如carlosfigueira和vanfosson:

        public static Dictionary<TK, TV> ValueDiff2<TK, TV>(this Dictionary<TK, TV> dictionary,
            Dictionary<TK, TV> otherDictionary)
        {
            var result = new Dictionary<TK, TV>(dictionary.Count, dictionary.Comparer);
            foreach (var (key, value) in dictionary)
            {
                var otherValue = otherDictionary[key];
                if (!Equals(value, otherValue))
                {
                    result.Add(key, otherValue);
                }
            }

            return result;
        }

答案 9 :(得分:0)

config.xml

答案 10 :(得分:0)

如果您需要对两者进行全面比较(想想维恩图的结果),假设它存在于至少一个字典中,则给定键有四个离散结果

public enum KeyCompareResult
{
  ValueEqual,
  NotInLeft,
  NotInRight,
  ValueNotEqual,
}

要获取字典中的所有键,请使用 dictionary.Keys。要获取任一字典中的键集,请使用 Enumerable.Union,它将组合这些集并过滤掉重复项。

假设您想要一些更通用的方法,那么您可以将比较写为

public IEnumerable<KeyValuePair<TKey, KeyCompareResult>> GetDifferences<TKey, TValue>(
    IDictionary<TKey, TValue> leftDict,
    IDictionary<TKey, TValue> rightDict,
    IEqualityComparer<TValue> comparer = null)
{
    var keys = leftDict.Keys.Union(rightDict.Keys);
    comparer ??= EqualityComparer<TValue>.Default;
    return keys.Select(key =>
    {
        if (!leftDict.TryGetValue(key, out var left))
        {
            return KeyValuePair.Create<TKey, KeyCompareResult>(key, KeyCompareResult.NotInLeft);
        }
        else if (!rightDict.TryGetValue(key, out var right))
        {
            return KeyValuePair.Create<TKey, KeyCompareResult>(key, KeyCompareResult.NotInRight);
        }
        else if (!comparer.Equals(left, right))
        {
            return KeyValuePair.Create<TKey, KeyCompareResult>(key, KeyCompareResult.ValueNotEqual);
        }
        else
        {
            return KeyValuePair.Create<TKey, KeyCompareResult>(key, KeyCompareResult.ValueEqual);
        }
    });
}

除非您查看方法调用,否则左/右区别并不明显,因此,您可能希望将方法结果选择为消息或其他一些更有意义的数据结构

var left = new Dictionary<int, string> { { 1, "one" }, { 2, "two" }, { 4, "four" } };
var right = new Dictionary<int, string> { { 1, "one" }, { 2, "A different value" }, { 3, "three" } };

GetDifferences(left, right, StringComparer.InvariantCulture)
    .Display(); // or however you want to process the data
/*
Key Value
--- -------------
  1 ValueEqual 
  2 ValueNotEqual 
  4 NotInRight 
  3 NotInLeft 
*/

答案 11 :(得分:-1)

 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text.RegularExpressions;
 namespace algorithms
{
    public class Program
    {
        public static void Main(string[] args)
        {
            callAnnagrams();
            //callArrayChunkingStr();
            //callFizzBussStr();
            //callMaxChars();
            //callRevNumber();
            //string value = "Acheck";
            //Console.WriteLine("value" + value.IsStartWithA());
            //callPalindromeStringMethods();
            //callRevStringMethods();

        }
        public static void callRevStringMethods()
        {
            Console.WriteLine("Hello World!");
            string revString = RevStrings.RevString("You cannot convert an array of strings to an array of characters by just calling a method like ToCharArray");
            Console.WriteLine("reverse string " + revString);

            string revString2 = RevStrings.RevString2("You cannot convert an array of strings to an array of characters by just calling a method like ToCharArray");
            Console.WriteLine("reverse string2 " + revString2);

            string revString3 = RevStrings.RevString3("You cannot convert an array of strings to an array of characters by just calling a method like ToCharArray");
            Console.WriteLine("reverse string3 " + revString3);

            string revString4 = RevStrings.RevString4("You cannot convert an array of strings to an array of characters by just calling a method like ToCharArray");
            Console.WriteLine("reverse string4 " + revString4);

        }
        public static void callPalindromeStringMethods()
        {
            Console.WriteLine("Hello World!");
            bool blPalindrome = Palindrome.PalindromeString("abba");
            Console.WriteLine("string is Palindrome" + blPalindrome);
            bool blPalindrome1 = Palindrome.PalindromeString("dfasdf");
            Console.WriteLine("string is Palindrome" + blPalindrome1);
        }

        public static void callRevNumber()
        {

            Console.WriteLine("reversed -1123.567 value" + RevNumbers.RevNumber3(-1123.567));
            Console.WriteLine("reversed 1123.567 value" + RevNumbers.RevNumber3(1123.567));
            Console.WriteLine("reversed -1123.567" + RevNumbers.RevNumber2(-1123.567));
            Console.WriteLine("reversed 234 value" + RevNumbers.RevNumber(234));
            Console.WriteLine("reversed -234 value" + RevNumbers.RevNumber(-234));           

        }

        public static void callMaxChars()
        {
            //MaxChar.MaxCharsWithASCII("rwersfsdfsdfsdf");
            //MaxChar.MaxCharsWithASCII("testtestttettssssssssssstest");
            MaxChar.MaxCharsWithDictionary("testtestttettssssssssssstest");

        }
        public static void callFizzBussStr()
        {
            FizzBuss.FizzBussStr();
        }
        public static void callArrayChunkingStr()
        {
            int[] numArray = new int[] { 1, 3, 5, 6, 7, 8, 8,9 };
            ArrayList anum=new ArrayList { 'a','b','c','d','e' };
            ArrayChunking.ArrayChunkingStr2(anum, 3);
            Console.WriteLine();
            anum = new ArrayList { 1, 2, 3, 4, 5, 6, 7, 8,9 };
            ArrayChunking.ArrayChunkingStr2(anum, 2);
            Console.WriteLine();
            numArray = new int[] { 1, 2, 3, 4, 5 };
            ArrayChunking.ArrayChunkingStr(numArray, 10);
        }
        public static void callAnnagrams()
        {
            Annagram.Annagrams("this is a valid string ", "sa i hits daliv gnirts");
            Annagram.Annagrams("this is a valid string ", "ssdfasdfad dsfasdf453 $ ,fgdaliv gnirts");
            Annagram.Annagrams("string $ ONE ,", "snigtr # neo");

        }
    }
    public static class StringMethods
    {
        public static bool IsStartWithA(this string s)
        {
            return s.StartsWith('A');
        }
    }
    public class RevStrings
    {
        public static string RevString(string str)
        {
            var charArry = str.ToCharArray();
            Array.Reverse(charArry);
            return new string(charArry);
        }
        public static string RevString2(string str)
        {
            string revString = "";
            foreach( char c in str)
            {
                revString = c + revString;
            }

            return revString;
        }

        //no change to the order of the words
        public static string RevString3(string str)
        {
            string[] strArray = str.Split(' ');

            string revString = "";
            foreach (string s in strArray)
            {
                var charArray = s.ToCharArray();
                Array.Reverse(charArray);

                string reString = (strArray.Length > 1) ? new string(charArray) +" " : new string(charArray) + string.Empty;
                revString +=  reString;

            }

            return revString;
        }
        public static string RevString4(string str)
        {
            string[] strArray = str.Split(' ');

            string revString = "";
            foreach (string s in strArray)
            {
                string revWord = "";
                foreach(char c in s)
                {
                    revWord = c + revWord;
                }
                revString += revWord + " ";
            }

            return revString;
        }

    }

    public class Palindrome
    {
        public static bool PalindromeString(string str)
        {
            if (RevStrings.RevString3(str).ToUpper() == str.ToUpper())
                return true;
            else
                return false;
        }
    }

    public class RevNumbers
    {
        public static int RevNumber(int number)
        {
            string revStringNumber = RevStrings.RevString2(number.ToString().Replace("-", ""));

            Int32 num = Int32.Parse(revStringNumber) * Math.Sign(number);
            return num;
        }
        public static double RevNumber2(double number)
        {         
            string revStringNumber = RevStrings.RevString2(number.ToString().Replace("-", ""));
            double num = Convert.ToDouble(revStringNumber) * Math.Sign(number); 

            return num;
        }
        public static double RevNumber3(double number)
        {
            string[] strArray = number.ToString().Replace("-", "").Split('.');
            string revString = "";
            int i = 0;

            foreach (string s in strArray)
            {
                var charArray = s.ToCharArray();
                Array.Reverse(charArray);
                string reString = new string(charArray);
                if (i + 1 < strArray.Length && strArray[i].Length > 0)
                    revString += reString + ".";
                else
                    revString += reString;
                i++;
            }
            double num = Convert.ToDouble(revString.ToString().Replace("-", "")) * Math.Sign(number);

            return num;


        }


    }

    public class MaxChar
    {
        public static void MaxCharsWithASCII(string str)
        {
            int i = 0, l, max;
            int ascii;
            l = str.Length;
            int[] ch_freq = new int[255];

            for (i = 0; i < 255; i++)
            {
                ch_freq[i] = 0;
            }

            i = 0;
            while(i<l)
            {
                ascii = (int)str[i];
                ch_freq[ascii] += 1;

                i++;
            }

            max = 0;
            for(i=0; i<255; i++)
            {
                if (i != 32)
                {
                    if (ch_freq[i] > ch_freq[max])
                        max = i;
                }
            }
            Console.Write("The Highest frequency of character '{0}' is appearing for number of times : {1} \n\n", (char)max, ch_freq[max]);

        }
        public static void MaxCharsWithDictionary(string str)
        {
            int i = 0, l, max;
            l = str.Length;
            Dictionary<int, int> char_freq = new Dictionary<int, int>();

            i = 0;
            while (i < l)
            {
                if(!(char_freq.ContainsKey((int)str[i])))
                    char_freq.Add((int)str[i], 1);               
                else
                    char_freq[(int)str[i]]= char_freq[str[i]]+1;
                i++;
            }

            max = 0;
            for (i = 0; i < char_freq.Count; i++)
            {

                    if (char_freq[str[i]] > char_freq[str[max]])
                        max = i;

            }
            Console.Write("The Highest frequency of character '{0}' is appearing for number of times : {1} \n\n",(char)str[max], char_freq[str[max]]);

        }
        public static Dictionary<int,int> MaxCharsWithReturnDictionary(string str)
        {
            int i = 0, l, max;
            l = str.Length;
            Dictionary<int, int> char_freq = new Dictionary<int, int>();

            i = 0;
            while (i < l)
            {
                if (!(char_freq.ContainsKey((int)str[i])))
                    char_freq.Add((int)str[i], 1);
                else
                    char_freq[(int)str[i]] = char_freq[str[i]] + 1;
                i++;
            }

            max = 0;
            for (i = 0; i < char_freq.Count; i++)
            {

                if (char_freq[str[i]] > char_freq[str[max]])
                    max = i;

            }
            return char_freq;
        }

    }

    public class FizzBuss
    {
        public static void FizzBussStr()
        {
            double num =Convert.ToDouble(Console.ReadLine());
            for (int i = 1; i <= num; i++)
            {
                if ((i % 3 == 0) && (i % 5 == 0)) //by both
                    Console.WriteLine("FizzBuzz");
                else if (i % 3 == 0)  //by 3
                    Console.WriteLine("Fizz");
                else if( i % 5 == 0) //by 5
                    Console.WriteLine("Buzz");
                else Console.WriteLine(i);
            }

        }
    }

    public class ArrayChunking
    {
        public static ArrayList ArrayChunkingStr2(ArrayList intArray, int chunk)
        {
            ArrayList arrList = new ArrayList();

            int len = intArray.Count;
            int div = len / chunk;
            int howManyChunks = (len % chunk) > 0 ? div + 1 : div;
            int chkInc = 0;
            for (int chkk = 0; chkk < howManyChunks; chkk++)
            {
                ArrayList arr = new ArrayList();
                for (int i = 0; i < chunk; i++)
                {
                    if ((i + chkInc) < len)
                    {
                        arr.Insert(i, intArray[i + chkInc]);
                    }
                }
                chkInc += chunk;
                arrList.Add(arr);
            }

            if (howManyChunks > 0)
            {
                //Console.Write("[");
                string disarr = "[";
                for (int j = 0; j < howManyChunks; j++)
                {
                    //Console.Write("[");
                    disarr += "[";

                    ArrayList x = (ArrayList)arrList[j];
                    string dis = "";
                    for (int i = 0; i < x.Count; i++)
                    {
                        dis += x[i].ToString() + ",";
                    }
                    dis = dis.TrimEnd(',');
                    disarr += dis + "],";
                    //Console.Write(disarr);
                }
                disarr = disarr.TrimEnd(',');
                //Console.Write("]");
                disarr += "]";
                Console.Write(disarr);
            }
            return arrList;
        }
        public static ArrayList ArrayChunkingStr(int[] intArray,int chunk)
        {
            ArrayList arrList = new ArrayList();

            int len = intArray.Length;
            int div = len / chunk;
            int howManyChunks = (len % chunk) > 0 ? div + 1: div;
            int chkInc = 0;
            for(int chkk=0; chkk < howManyChunks; chkk++)
            {
                ArrayList arr = new ArrayList();
                for (int i = 0; i < chunk; i++)
                {
                    if ((i + chkInc) < len)
                    {
                        arr.Insert(i,intArray[i + chkInc]);
                    }
                }
                chkInc += chunk;
                arrList.Add(arr);
            }

            if (howManyChunks > 0)
            {
                //Console.Write("[");
                string disarr = "[";
                for (int j = 0; j < howManyChunks; j++)
                {
                    //Console.Write("[");
                    disarr += "[";

                    ArrayList x = (ArrayList)arrList[j];
                    string dis = "";
                    for (int i = 0; i < x.Count; i++)
                    {
                        dis += x[i].ToString() + ",";
                    }
                    dis = dis.TrimEnd(',');
                    disarr += dis + "],";
                    //Console.Write(disarr);
                }
                disarr = disarr.TrimEnd(',');
                //Console.Write("]");
                disarr += "]";
                Console.Write(disarr);
            }
            return arrList;
        }
    }

    public class Annagram
    {
        public static bool Annagrams(string str1, string str2)
        {

            str1 = Regex.Replace(str1.ToLower(), "[^a-zA-Z0-9]+", "", RegexOptions.Compiled);
            str2 = Regex.Replace(str2.ToLower(), "[^a-zA-Z0-9]+", "", RegexOptions.Compiled);

            Dictionary<int, int> str1Dic = MaxChar.MaxCharsWithReturnDictionary(str1.ToLower());

            Dictionary<int, int> str2Dic = MaxChar.MaxCharsWithReturnDictionary(str2.ToLower());
            if (str1Dic.Count != str2Dic.Count)
            {
                Console.WriteLine("strings are not annagrams");
                return false;
            }
            else
            {
                if (!(str1Dic.Keys.All(str2Dic.ContainsKey)))
                {
                    Console.WriteLine("strings are not annagrams");
                    return false;
                }
                else
                {
                    var dict3 = str2Dic.Where(entry => str1Dic[entry.Key] != entry.Value)
                            .ToDictionary(entry => entry.Key, entry => entry.Value);
                    if (dict3.Count > 0)
                    {
                        Console.WriteLine("strings are not annagrams");
                        return false;
                    }
                    else
                    {
                        Console.WriteLine("strings are  annagrams");
                        return true;
                    }
                }


            }

        }
    }
}