【问题标题】:c# - Compare two Dictionary for know each "add" operations and each "remove" operationsc# - 比较两个字典以了解每个“添加”操作和每个“删除”操作
【发布时间】:2011-09-13 21:04:06
【问题描述】:

我有 2 个结构相似的字典

Dictionary<string, List<int>> Origins

Dictionary<string, List<int>> Changes

我一开始就创建了起源。它是初始状态的副本。 示例:

Origins["toto"] = new List<int>(){1,2,3};
Origins["tata"] = new List<int>();
Origins["titi"] = new List<int>(){1,2};

在用户操作之后,我将更改保存在 Changes 字典中。 基本上,用户可以添加或删除一些链接到字符串的数字。所以我保留了这样的任何变化的所有痕迹:

示例:如果用户在“tata”中添加 1 在字典中改变了一个有 “塔塔”有 1 个

如果用户在“toto”中添加 4 在字典中改变了一个有 "toto" 有 1,2,3,4

如果用户删除“titi”中的 1 在字典中改变了一个有 “titi”有 2 个

我需要更改字典来了解用户何时返回原始状态,并进行简单的比较。

如果没有对字符串进行任何更改,则更改字典没有该字符串的任何条目。

多次更改后,用户可以保存更改。 所以现在我需要找到所有的添加和删除操作。

我的第一个想法是比较两个字典,看看添加操作和删除操作。但是如何? 如果我比较相同字符串的列表,我可能知道区别,但我被困在这里。 也许有更好的方法来做到这一点?有什么建议吗?

【问题讨论】:

    标签: c# .net .net-3.5 dictionary change-tracking


    【解决方案1】:

    您有以下情况:

    1. 一个新的字符串键被添加到Changes,以及一些相关的值。
    2. 现有字符串键的值已更改(添加或删除值)。

    在情况 (1) 中,您将在 Changes 中有一个在 Origins 中不存在的条目。在情况 (2) 中,您将在两者中都有一个条目,但具有不同的值列表。

    (我要假设的值是一个数学集合,即一个特定的值只能出现一次,并且排序无关紧要。如果不是这种情况,那么您将不得不稍微修改该方法。)

    要检测案例 (1),您可以找到唯一的键:

    IEnumerable<string> newKeys = Changes.Keys.Except(Origins.Keys);
    

    显然,Changes 中的每个新键值都需要“添加”。您可以简单地迭代 newKeys 枚举并从 Changes 检索值:

    foreach (string key in newKeys)
    {
        IEnumerable<int> addedValues = Changes[key];
        // your processing here
    }
    

    要检测案例 (2),您需要迭代字典并比较 Changes with Origins 中的值集。为此,我们将迭代 Origins 以获取键和原始值,然后使用来自 Changes 的键检索项目。 (我们将这样做,因为如果我们迭代Changes,我们可能会最终得到Origins 中不存在的新添加键,这是我们必须处理的另一种情况。)

    foreach (KeyValuePair<string, List<int>> entry in Origins)
    {
        List<int> originsValues = entry.Value;
        List<int> changesValues;
    
        // handle no key in Changes (as pointed out by Guillaume V). 
        if (!Changes.TryGet(entry.Key, out changesValues)) changesValues = originsValues;
    
        IEnumerable<int> removedValues = originsValues.Except(changesValues);
        IEnumerable<int> addedValues = changesValues.Except(originsValues);
    
        // your processing here
    }
    

    【讨论】:

    • 列表 changesValues = Changes[entry.Key].Value;这可能会崩溃,因为更改可能没有键“entry.key”
    【解决方案2】:

    你可以试试这个:

            Dictionary<string, List<int>> Origin = new Dictionary<string, List<int>>();
    
            Origin["toto"] = new List<int>(){1,2,3};
            Origin["tata"] = new List<int>();
            Origin["titi"] = new List<int>(){1,2};
    
    
            Dictionary<string, List<int>> Changes = new Dictionary<string,List<int>>();
    
            Changes["toto"] = new List<int>() { 1, 2, 3, 4 };
            Changes["tata"] = new List<int>(){1};
            Changes["titi"] = new List<int>() { 2 };
    
            Dictionary<string, List<int>> ToRemove = new Dictionary<string, List<int>>();
            Dictionary<string, List<int>> ToAdd = new Dictionary<string, List<int>>();
    
            foreach (string key in Origin.Keys)
            {
                ToRemove[key] = Origin[key];
                ToAdd[key] = Changes[key];
    
    
                foreach (int i in ToRemove[key])
                {
    
                    if (ToAdd[key].Contains(i)) //There is no change
                    {
                        ToAdd[key].Remove(i);
                        ToRemove[key].Remove(i);
                    }
                }
            }
    

    【讨论】:

    • ToAdd[key] = Changes[key];这可能会崩溃,因为更改可能没有键“key”
    【解决方案3】:

    如果您只需要确定两个对象是否相等,我建议您创建自己的类,并覆盖 Equals()GetHashInfo()

    public class ComparableDictionary : Dictionary<string, List<int>>
    {
        private const int CouldBeAnyConstant = 392;
    
        public override bool Equals(object other)
        {
            return Equals((ComparableDictionary)other);
        }
    
        public bool Equals(ComparableDictionary other)
        {
            return other != null && (GetHashCode() == other.GetHashCode());
        }
    
        public override int GetHashCode()
        {
            int result = CouldBeAnyConstant;
            unchecked
            {
                foreach (var list in Values)
                    foreach (var value in list)
                        result = result*value.GetHashCode();
    
                foreach (var value in Keys)
                    result = result * value.GetHashCode();
            }
    
            return result;
        }
    

    }

    那么你所要做的就是使用它:

    public bool UserHasMadeChanges(ComparableDictionary Origins, ComparableDictionary Changes)
    {
        return !Origins.Equals(Changes)
    }
    

    【讨论】:

      【解决方案4】:

      受 Paul Ruane 和 matmot 的启发,我开发了自己的方式:

      foreach (var change in this.Changes)
              {
                  List<int> origin = this.Origins[change.Key];
      
                  List<int> newValue = change.Value;
      
                  //find the basic add and remove
                  IEnumerable<int> remove = origin.Except(newValue);
                  IEnumerable<int> add = newValue.Except(origin);
      
                  if (!add.Any() && remove.Any())
                  {
                    //remove all in the remove list 
                      continue;
      
                  }
                  else if (add.Any() && !remove.Any())
                  {
                    //add all in the add list
                      continue;
                  }
      
                  //if in the same change there are add and remove 
                  IEnumerable<int> dif1 = add.Except(remove);
                  IEnumerable<int> dif2 = remove.Except(add);
      
                  if (dif1.Any())
                  {
                      //add all in the dif1 list 
      
                  }
                  if (dif2.Any())
                  {
                      //remove all in  dif2 list
      
      
                  }
              }
      

      您对此有何看法?

      【讨论】:

      • 当您尝试从 Origins 检索不存在的值时,即在针对新键添加值的情况下,它会抛出。
      • 另外,您似乎在 IEnumerable&lt;T&gt; 上使用了名为 IsNullOrEmpty 的扩展方法。但是,该值不可能是 null,因为 Enumerable.Except() 不会返回 null。因此,您可以在此处使用现有的 Enumerable.Any() 扩展方法来检查是否有任何元素。
      • @Paul Ruane 。Enumerable.Any() 很好。我将编辑我的帖子。但是,在我的情况下,Origins 包含所有可能的密钥。这是一个很好的cmets。谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 2019-09-02
      • 2019-05-19
      • 1970-01-01
      相关资源
      最近更新 更多